进入uniapp

This commit is contained in:
2023-12-01 23:57:44 +08:00
parent a78893ea1f
commit d61f5e6c92
524 changed files with 88081 additions and 12 deletions

3
.gitignore vendored
View File

@@ -9,7 +9,8 @@
**/node_modules/**
hs-im-server/.vs/
hs-im-server/hs-im-server.7z
im-uniapp/
hs-im-server/im-common/im-common.iml
hs-im-server/im-service/im-service.iml
hs-im-server/im-tcp/im-tcp.iml
hs-im-server/im-system.iml

View File

@@ -16,7 +16,7 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: io.netty:netty-all:4.1.35.Final" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.51" level="project" />
<orderEntry type="module" module-name="im-common" />
<orderEntry type="library" name="Maven: com.lld:im-common:1.0.0-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.8.RELEASE" level="project" />

View File

@@ -50,7 +50,7 @@
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.github.jeffreyning:mybatisplus-plus:1.5.1-RELEASE" level="project" />
<orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.0.6" level="project" />
<orderEntry type="module" module-name="im-common" />
<orderEntry type="library" name="Maven: com.lld:im-common:1.0.0-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.9" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.13" level="project" />
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />

View File

@@ -42,8 +42,6 @@ spring:
size: 36
checkout-timeout: 0
# logger 配置
logging:
config: classpath:logback-spring.xml

View File

@@ -1,3 +1,3 @@
spring:
profiles:
active: dev
active: prod

View File

@@ -101,8 +101,8 @@ mqQueueName: 123
mybatis-plus:
configuration:
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 会正常输出日志
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 会正常输出日志
# log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
mapper-locations: classpath*:mapper/*.xml
global-config:
db-config:

View File

@@ -1,3 +1,3 @@
spring:
profiles:
active: prod
active: dev

View File

@@ -79,7 +79,7 @@
</Pattern>
</layout>
</appender>
<root level="WARN">
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="fileAppender"/>
</root>

8
im-uniapp/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
/.hbuilderx/
/node_modules/
/unpackage/dist/
/package-lock.json
/unpackage/release/
/unpackage/cache/
/json/
/unpackage/debug/

99
im-uniapp/App.vue Normal file
View File

@@ -0,0 +1,99 @@
<script>
import {
version,
name
} from './package.json'
import {
versionName
} from '@/manifest.json'
import {appInit} from "@/common/utils.js"
import consoleImgs from '@/common/consoleImgs.js'
// #ifdef APP-PLUS
import appUpgrade from '@/common/appUpgrade.js';
const TUICalling = uni.requireNativePlugin("TUICallingUniPlugin-TUICallingModule");
// #endif
export default {
onLaunch: function() {
localStorage.setItem("appId",10000)
appInit(500).then((res)=>{
console.log("获取到token创建长链接")
this.$store.dispatch('getAddressList');
setTimeout(()=>{
this.$socketTask.connectSocket()
uni.setStorageSync("initReady",1)
},1000)
}).then((error)=>{
console.log("未获取到IM的token")
console.log(error)
})
console.log(consoleImgs.fz)
// todo 下列两行
uni.setStorageSync('device', 'H5');
uni.setStorageSync('version', versionName);
},
onShow: function() {
console.log('App Show')
uni.getStorage({
key: 'call',
success: (res) => {
var callx=res.data
if(callx){
var call=JSON.parse(callx)
function getInervalHour(startDate) {//获取两个时间之间的小时
if (!startDate) {
return '0秒'
}
var ms = new Date().getTime() - startDate;
if (ms < 0) return '0秒';
if((ms/1000)<60){
return Math.floor(ms / 1000)+'';
}else{
return Math.floor(ms / 1000 /60)+'';
}
}
var msgType=''
if(call.type=='audio'){
msgType='TRTC_VOICE_END'
}
if(call.type=='video'){
msgType='TRTC_VIDEO_END'
}
this.$fc.pushOutMsg({
msgContent:getInervalHour(call.startTime),
msgType:msgType,
windowType:'SINGLE',
userId:call.userId,
})
uni.removeStorageSync('call')
}
}
});
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style lang="scss">
/*每个页面公共css */
@import '@/uni_modules/uni-scss/index.scss';
@import "@/static/styles/animation.css";
/* #ifndef APP-NVUE */
@import '@/static/customicons.css';
// 设置整个项目的背景色
page {
box-sizing: border-box;
}
/* #endif */
// 以下内容最好转移到单页
</style>

373
im-uniapp/LICENSE Normal file
View File

@@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

70
im-uniapp/README.md Normal file
View File

@@ -0,0 +1,70 @@
#### 一、项目简介
+ 一直以来都有一个社交梦想做一款IM应用看了很多优秀的开源项目但是没有合适自己的。于是利用休息时间自己写了这么一套系统。
+ 项目第一个版本历时2个月前端使用`uniapp`,后端使用`SpringBoot`
+ 页面设计后期会出`独立UI`
+ 手机端使用`uniapp`实现,目前仅支持`安卓端``iOS端``H5端`,后期会继续适配`小程序端``桌面端`windows、mac`web端`
+ 您的支持,就是我们`【生发的动力】`,请手动点个`star`吧。
+ 前端源码地址:[https://gitee.com/lakaola/im-uniapp](https://gitee.com/lakaola/im-uniapp)
+ 后端源码地址:[https://gitee.com/lakaola/im-platform](https://gitee.com/lakaola/im-platform)
+ 下载体验地址:[https://im.q3z3.com/](https://im.q3z3.com/)
+ 加入QQ群
[![Q群3](https://img.shields.io/badge/Q群3-60559794-blue.svg)](https://jq.qq.com/?_wv=1027&k=etm5kfCT)60559794
[![Q群2](https://img.shields.io/badge/Q群2-671590862-blue.svg)](https://jq.qq.com/?_wv=1027&k=9VJv8p2I)671590862(已满)
[![Q群1](https://img.shields.io/badge/Q群1-535099683-blue.svg)](https://jq.qq.com/?_wv=1027&k=PQMnFugm)535099683(已满)
#### 二、使用须知
+ 仅允许`技术学习`使用
+ 不允许`本产品及其衍生品`进行任何形式`商业使用`
+ 请自觉遵守本开源协议MPL-2.0),再次开源请注明出处
+ 推荐Watch、Star项目获取项目第一时间更新同时也是对项目最好的支持
+ 希望大家多多支持本开源作品
#### 三、技术使用
+ 推送uniPush + websocket
+ 资源阿里OSS图片、声音、视频、文件等
+ 音视频TRTC
+ 地图:高德地图
+ 短信:阿里云短信
+ 后端Hutool、MyBatis-Plus、shiro、sharding-jdbc、接口版本控制等
+ 前端uniapp(Vue3)
#### 四、演示效果
<img src="https://img.alicdn.com/imgextra/i2/87413133/O1CN01mD2wwN1Z0xctYYdAA_!!87413133.jpg" width="200">
<img src="https://img.alicdn.com/imgextra/i3/87413133/O1CN01bZSz2q1Z0xco96F1t_!!87413133.jpg" width="200">
<img src="https://img.alicdn.com/imgextra/i3/87413133/O1CN01Pe8G6S1Z0xcmQluDI_!!87413133.jpg" width="200">
<img src="https://img.alicdn.com/imgextra/i1/87413133/O1CN012JP8VW1Z0xccuWKzM_!!87413133.jpg" width="200">
<img src="https://img.alicdn.com/imgextra/i4/87413133/O1CN01fMUNJA1Z0xck1w0kt_!!87413133.jpg" width="200">
<img src="https://img.alicdn.com/imgextra/i3/87413133/O1CN01n8MZhZ1Z0xctYZEbM_!!87413133.jpg" width="200">
#### 五、项目计划
+ 适配android端已完成
+ 适配iOS端已完成
+ 适配H5端已完成
+ 适配PC端[Windows]
+ 适配PC端[Mac]
+ 适配WEB端[后台管理集成通信]
+ 增加后台管理端[管理后台]
+ 更换手机端UI[独立UI]
+ 增加通知栏消息展示[当app后台挂起通知栏展示消息]
+ 各大应用市场上架
+ 后端优化,支持大并发
+ ...
#### 六、版本迭代
+ 1.2.0
1、修复群名称、昵称显示问题
2、修复性别显示问题
3、修复部分手机日期显示问题
4、修改消息滚动问题
+ 1.1.0
1、适配H5端
2、集成WebSocket通讯
3、修改注册后不跳转至登录页问题
4、修改长按消息弹出方式
5、修改群聊个人头像点击跳转无信息问题
6、修改附近的人显示问题
7、修改摇一摇显示问题
8、修改其他问题
+ 1.0.0
1、首版震撼发布

View File

@@ -0,0 +1,213 @@
var _maskView, _contentView, _downloadTask, _loadingProgress, _screenHeight, _screenWidth, _config = {
forceUpgrade: false,
titleText: "版本更新",
content: "",
contentAlign: "left",
loadingColor: "#329EEE",
cancelText: "暂不升级",
cancelColor: "#666",
confirmText: "立即升级",
confirmColor: "#329EEE",
windowHeight: 380,
packageUrl: "",
browser: false,
maskColor: "rgba(0,0,0,0.3)"
},
_calculatePosition = function() {
return {
top: (_screenHeight - _config.windowHeight) / 2,
left: _screenWidth * .05,
width: _screenWidth * .9,
right: _screenWidth * .05,
height: _config.windowHeight
}
},
_createMask = function() {
_maskView = new plus.nativeObj.View("maskView", {
top: "0px",
left: "0px",
width: "100%",
height: "100%",
backgroundColor: _config.maskColor
})
},
_createContentView = function() {
var calculatePosition = _calculatePosition();
_contentView = new plus.nativeObj.View("contentView", {
top: calculatePosition.top + "px",
left: calculatePosition.left + "px",
height: _config.windowHeight + "px",
width: calculatePosition.width + "px",
overflow: "auto"
});
_contentView.drawRect({
color: "#ffffff",
radius: "20px"
}, {
width: "100%",
height: "100%"
}, "roundedRect");
_contentView.drawText(_config.titleText, {
top: "20px",
height: "20px",
}, {
size: "16px",
color: "#333",
align: "center",
}, "titleText");
_contentView.drawText(_config.content, {
top: "60px",
left: "20px",
right: "20px",
height: _config.windowHeight - 120 + "px",
}, {
size: "16px",
color: "#666",
align: _config.contentAlign,
verticalAlign: "top",
whiteSpace: "normal",
overflow: "ellipsis"
}, "UpdaterContent");
_createLoading(-2);
var top = _config.windowHeight - 60 + 15;
var fontSize = '16px';
if (!_config.forceUpgrade) {
_contentView.drawRichText("<font style=\"font-size:" + fontSize + ";\" color=\"" + _config.cancelColor +
"\">" + _config
.cancelText + "</font>", {
width: "50%",
top: top + "px",
left: "0px"
}, {
align: "center",
onClick: function() {
close()
}
}, "cancel");
_contentView.drawRichText("<font color=\"" + _config.confirmColor +
"\" style=\"font-size:" + fontSize + ";\">" +
_config.confirmText + "</font>", {
width: "50%",
right: "0px",
top: top + "px"
}, {
align: "center",
onClick: function() {
_config.packageUrl ? "android" === plus.os.name.toLowerCase() ? _config.browser ? (plus
.runtime.openURL(_config.packageUrl)) : _createTask() : plus.runtime.openURL(
_config.packageUrl) : uni.showToast({
title: "安装包地址为空",
icon: "none"
})
}
}, "submit")
} else {
_contentView.drawRichText("<font color=\"" + _config.confirmColor + "\" style=\"font-size:" + fontSize +
";\">" +
_config.confirmText + "</font>", {
width: "100%",
right: "0px",
top: top + "px"
}, {
align: "center",
onClick: function() {
_config.packageUrl ? "android" === plus.os.name.toLowerCase() ? _config.browser ? (plus
.runtime.openURL(_config.packageUrl)) : _createTask() : plus.runtime.openURL(
_config.packageUrl) : uni.showToast({
title: "安装包地址为空",
icon: "none"
})
}
}, "submit")
}
},
_createLoading = function(progress) {
var calculatePosition = _calculatePosition();
var top = _config.windowHeight - 65;
var width = 0 <= progress ? (calculatePosition.width - 100) / 100 * progress : 0;
width = parseInt(width);
var text = 100 <= progress ? "下载完成" : "下载中...";
var loadingText = "";
loadingText = -1 == progress ? "资源加载中..." : 0 <= progress ? text + "(" + progress + "%)" : "";
_contentView.drawRect({
color: _config.loadingColor
}, {
width: width + "px",
height: "3px",
left: "20px",
top: top + "px"
}, "loading");
_contentView.drawRichText("<font color=\"" + _config.loadingColor + "\">" + loadingText + "</font>", {
width: "100px",
top: top + "px",
left: width + "px"
}, {
align: "center"
}, "loadingText")
},
_createTask = function() {
return _downloadTask ? void console.log("正在下载中") : void(_createLoading(-1),
_downloadTask =
uni.downloadFile({
url: _config.packageUrl,
success: function(res) {
if (200 === res.statusCode) {
var tempFilePath = res.tempFilePath;
uni.saveFile({
tempFilePath: tempFilePath,
success: function(res) {
plus.runtime.install(res.savedFilePath, {
force: true
}, function(res) {
console.log('安装包信息' + JSON.stringify(res))
}, function(res) {
uni.showToast({
title: '安装失败,请检查下载链接',
icon: 'none',
duration: 3000
});
});
close();
}
})
}
}
}), _downloadTask.onProgressUpdate(function(res) {
_loadingProgress != res.progress && (_loadingProgress = res.progress, _createLoading(res
.progress));
}))
},
init = function(option) {
_screenHeight = plus.screen.resolutionHeight;
_screenWidth = plus.screen.resolutionWidth;
_downloadTask = null;
option.titleText && (_config.titleText = option.titleText);
option.windowHeight && (_config.windowHeight = option.windowHeight);
option.forceUpgrade && (_config.forceUpgrade = option.forceUpgrade);
option.content && (_config.content = option.content);
option.contentAlign && (_config.contentAlign = option.contentAlign);
option.loadingColor && (_config.loadingColor = option.loadingColor);
option.cancelText && (_config.cancelText = option.cancelText);
option.cancelColor && (_config.cancelColor = option.cancelColor);
option.confirmText && (_config.confirmText = option.confirmText);
option.confirmColor && (_config.confirmColor = option.confirmColor);
option.packageUrl && (_config.packageUrl = option.packageUrl);
option.browser && (_config.browser = option.browser);
option.maskColor && (_config.maskColor = option.maskColor);
_createMask();
_createContentView();
},
show = function() {
_maskView && _maskView.show();
_contentView && _contentView.show();
},
close = function() {
_downloadTask && (_downloadTask.abort(), _downloadTask = null, _createLoading(-2));
_maskView && _maskView.hide();
_contentView && _contentView.hide();
};
export default {
init: init,
show: show,
close: close
}

210
im-uniapp/common/browser.js Normal file
View File

@@ -0,0 +1,210 @@
export default {
init: function(options) {
this.initWebview(options)
this.handleEvent()
return this
},
initWebview: function(options) {
var hh=plus.screen.height-44
var _self = this
_self.webview = plus.webview.create('', 'browser', {
scalable:true,
height:hh+'px',
bottom:'44px',
background:'#ff5500',
titleNView: {
backgroundColor: '#FFFFFF',
progress: { //进度条
color: '#4678e7',
height: '3px'
},
splitLine: { //底部分割线
color: '#cccccc',
height: '1px'
},
buttons: [{ //关闭按钮
'float': 'left',
fontSrc: '/static/uni.ttf',
text: '\ue460',
onclick: _self.close.bind(_self)
}, { //更多按钮
'float': 'right',
fontSrc: '/static/uni.ttf',
text: '\ue507',
onclick: _self.more.bind(_self)
}]
},
additionalHttpHeaders: options.headers || {}
})
// 配置下拉刷新
_self.webview.setPullToRefresh({
support: true
}, function() {
_self.reload()
var titleUpdate = function() {
setTimeout(function() {
_self.webview.endPullToRefresh()
_self.webview.removeEventListener('titleUpdate', titleUpdate)
}, 300)
};
_self.webview.addEventListener('titleUpdate', titleUpdate)
})
//绘制返回前进按钮
this.navBottom = new plus.nativeObj.View("navBottom", {
bottom: '0px',
left: '0px',
height: '44px',
width: '100%',
backgroundColor: 'rgb(255,255,255)'
})
var screenWidth = plus.screen.resolutionWidth
var left = screenWidth / 4
this.navBottom.draw([{
tag: 'font',
id: 'back',
text: '\ue471',
textStyles: {
fontSrc: '/static/uni.ttf',
size: '24px',
color: '#000000'
},
position: {
right: left+48,
height: '100%',
}
},
{
tag: 'font',
id: 'forward',
text: '\ue470',
textStyles: {
fontSrc: '/static/uni.ttf',
size: '24px',
color: '#000000'
},
position: {
left: left,
height: '100%',
}
}
])
//点击前进或后退
this.navBottom.addEventListener("click", (e) => {
if (e.clientX > left && e.clientX < (left + 24)) {
_self.back()
}
if (e.clientX > (screenWidth - left - 12) && e.clientX < (screenWidth - left + 12)) {
_self.forward()
}
})
//监听页面变化
_self.webview.addEventListener('loaded', function() {
_self.webview.canBack(function(event) {
var canBack = false
if (event.canBack) {
canBack = true
_self.navBottom.show()
} else {
canBack = false
}
_self.webview.canForward(function(event) {
if (event.canForward) {
_self.navBottom.drawText('\ue470', {
left: left,
height: '100%'
}, {
color: '#000000',
fontSrc: '/static/uni.ttf',
size: '24px'
}, 'forward')
} else {
_self.navBottom.drawText('\ue470', {
left: left,
height: '100%'
}, {
color: '#EEEEEE',
fontSrc: '/static/uni.ttf',
size: '24px'
}, 'forward')
}
if (!canBack && !event.canForward) {
// _self.navBottom.hide()
}
})
})
_self.navBottom.show()
}, false)
},
handleEvent: function(url) {
var _self = this
plus.key.addEventListener('backbutton', function() {
_self.close()
return
var topWebview = plus.webview.getTopWebview()
// 不等于浏览器窗口
if (topWebview.id !== 'browser') {
// 这里除了浏览器窗口就是首页了,直接退出了;
plus.runtime.quit()
} else {
_self.back()
}
})
},
show: function(url) {
url = url || 'https://www.baidu.com'
this.webview.loadURL(url)
this.webview.show('slide-in-right')
},
more: function() {
var _self = this
uni.showActionSheet({
itemList: ['刷新', '浏览器打开'],
success: function(res) {
if (res.tapIndex == 0) {
_self.reload()
} else if (res.tapIndex == 1) {
plus.runtime.openURL(_self.webview.getURL())
}
}
})
},
reload: function() {
// 刷新
this.webview.reload(true)
},
back: function() {
//后退
var _self = this
_self.webview.canBack(function(event) {
if (event.canBack) {
_self.webview.back()
} else {
_self.close()
}
})
},
forward: function() {
//前进
var _self = this
_self.webview.canForward(function(event) {
if (event.canForward) {
_self.webview.forward();
} else {
plus.nativeUI.toast('没有可前进的地址')
}
})
},
close: function() {
//关闭
// this.navBottom.hide()
this.navBottom.close()
this.navBottom=null;
this.webview.close('browser', 'slide-out-right')
this.webview.clear()
plus.key.removeEventListener("backbutton", function() {
});
}
}

30165
im-uniapp/common/city.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
export default class config{
static prod="online_not"
static getZnzqApiUrl(){
if(process.env.NODE_ENV === 'development' ){
return 'http://work.ii999.live:19002/znzq'
}else{
if(this.prod=="online"){
return 'http://work.ii999.live:19002/znzq'
}else{
return '/znzq'
}
}
}
static getBaseApiUrl(){
//return process.env.NODE_ENV === 'development' ? 'http://119.45.242.222:8989' :'http://172.16.2.3:8989';
return process.env.NODE_ENV === 'development' ? 'http://119.45.242.222:8989' :'/base_api';
}
static getImApiUrl(){
//return process.env.NODE_ENV === 'development' ? 'http://119.45.242.222:28000/v1' :'http://172.16.2.3:28000/v1';
return process.env.NODE_ENV === 'development' ? 'http://119.45.242.222/im_api/v1' :'/im_api/v1';
}
static getImAppId(){
return 10000;
}
static getVideoRoomUrl(room,userid,isPublish=0,isPlayer=1,hasVideo=1,uname,tuid){
if(process.env.NODE_ENV === 'development' ){
return `http://localhost:8080/hd-glasses-app/#/call_room?showInviteConfirmDialog=1&isForce=0&isPublish=${isPublish}&isPlayer=${isPlayer}&uid=${userid}&room=${room}&hasVideo=${hasVideo}&uname=${uname}&tuid=${tuid}`
}else{
if(this.prod=="online"){
return `https://119.45.242.222/hd-glasses-app/#/call_room?showInviteConfirmDialog=1&isForce=0&isPublish=${isPublish}&isPlayer=${isPlayer}&uid=${userid}&room=${room}&hasVideo=${hasVideo}&uname=${uname}&tuid=${tuid}`
}else{
return `https://172.16.3.18/hd-glasses-app/#/call_room?showInviteConfirmDialog=1&isForce=0&isPublish=${isPublish}&isPlayer=${isPlayer}&uid=${userid}&room=${room}&hasVideo=${hasVideo}&uname=${uname}&tuid=${tuid}`
}
}
}
static getSRSUrl(){
if(process.env.NODE_ENV === 'development' ){
return 'webrtc://119.45.242.222/'
}else{
if(this.prod=="online"){
return 'webrtc://119.45.242.222/'
}else{
return 'webrtc://172.16.3.18/'
}
}
}
}

View File

@@ -0,0 +1,3 @@
export default {
fz:' ......................阿弥陀佛......................\n' + ' _oo0oo_ \n' + ' o8888888o \n' + ' 88" . "88 \n' + ' (| -_- |) \n' + ' 0\\ = /0 \n' + ' ___/---\\___ \n' + " .' \\| |/ '. \n" + ' / \\\\||| : |||// \\ \n' + ' / _||||| -卍-|||||_ \\ \n' + ' | | \\\\\\ - /// | | \n' + " | \\_| ''\\---/'' |_/ | \n" + " \\ .-\\__ '-' ___/-. / \n" + " ___'. .' /--.--\\ '. .'___ \n" + ' ."" < .___\\_<|>_/___.> "". \n' + ' | | : - \\.;\\ _ /;./ - : | | \n' + ' \\ \\ _. \\_ __\\ /__ _/ .- / / \n' + ' =====-.____.___ \\_____/___.-___.-===== \n' + ' =---= \n' + ' \n' + '....................佛祖保佑 ,永无BUG...................'
}

View File

@@ -0,0 +1,47 @@
import Request from 'luch-request' // 使用npm
import conf from "@/common/config.js"
const customHttp = new Request({
timeout: 300000, //超时时长5分钟,
header: {
'Content-Type': 'application/json;charset=UTF-8;'
}
});
//请求拦截器
customHttp.interceptors.request.use((config) => {
if (config.custom.target != undefined) {
if (config.custom.target == "znzq") {
config.baseURL = conf.getZnzqApiUrl()
const token = uni.getStorageSync('token');
config.header["X-Access-Token"] = token;
console.log("==================customHttp.interceptors.request.use")
console.log(config)
}
}
console.log(config)
return config
}, error => {
return Promise.resolve(error)
})
// 响应拦截器
customHttp.interceptors.response.use((response) => {
console.log(response)
return response
}, (error) => {
//未登录时清空缓存跳转
if (error.statusCode == 401) {
uni.clearStorageSync();
uni.switchTab({
url: "/pages/index/index.vue"
})
}
return Promise.resolve(error)
})
export default {
customHttp
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,17 @@
function getIM(){
// UNIAPP 环境
if(typeof uni !== 'undefined'){
return {im:uni['im'], im_webtoolkit:uni['im_webtoolkit']};
}
throw Error(" Couldn't not fetch lim.");
}
const lim = {
im: getIM().im,
};
export default {
lim
}

259
im-uniapp/common/md5.js Normal file
View File

@@ -0,0 +1,259 @@
/*
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
/*
* These are the functions you'll usually want to call
* They take string arguments and return either hex or base-64 encoded strings
*/
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
/*
* Perform a simple self-test to see if the VM is working
*/
function md5_vm_test()
{
return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}
/*
* Calculate the MD5 of an array of little-endian words, and a bit length
*/
function core_md5(x, len)
{
/* append padding */
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return Array(a, b, c, d);
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn(q, a, b, x, s, t)
{
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
/*
* Calculate the HMAC-MD5, of a key and some data
*/
function core_hmac_md5(key, data)
{
var bkey = str2binl(key);
if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
return core_md5(opad.concat(hash), 512 + 128);
}
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}
/*
* Convert a string to an array of little-endian words
* If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
*/
function str2binl(str)
{
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz)
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
return bin;
}
/*
* Convert an array of little-endian words to a string
*/
function binl2str(bin)
{
var str = "";
var mask = (1 << chrsz) - 1;
for(var i = 0; i < bin.length * 32; i += chrsz)
str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
return str;
}
/*
* Convert an array of little-endian words to a hex string.
*/
function binl2hex(binarray)
{
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++)
{
str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
}
return str;
}
/*
* Convert an array of little-endian words to a base-64 string
*/
function binl2b64(binarray)
{
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var str = "";
for(var i = 0; i < binarray.length * 4; i += 3)
{
var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
| (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
| ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
for(var j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
}
}
return str;
}
export default {
hex_md5
}

View File

@@ -0,0 +1,245 @@
/// null = 未请求1 = 已允许0 = 拒绝|受限, 2 = 系统未开启
var isIOS
function album() {
var result = 0;
var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
var authStatus = PHPhotoLibrary.authorizationStatus();
if (authStatus === 0) {
result = null;
} else if (authStatus == 3) {
result = 1;
} else {
result = 0;
}
plus.ios.deleteObject(PHPhotoLibrary);
return result;
}
function camera() {
var result = 0;
var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
if (authStatus === 0) {
result = null;
} else if (authStatus == 3) {
result = 1;
} else {
result = 0;
}
plus.ios.deleteObject(AVCaptureDevice);
return result;
}
function location() {
var result = 0;
var cllocationManger = plus.ios.import("CLLocationManager");
var enable = cllocationManger.locationServicesEnabled();
var status = cllocationManger.authorizationStatus();
if (!enable) {
result = 2;
} else if (status === 0) {
result = null;
} else if (status === 3 || status === 4) {
result = 1;
} else {
result = 0;
}
plus.ios.deleteObject(cllocationManger);
return result;
}
function push() {
var result = 0;
var UIApplication = plus.ios.import("UIApplication");
var app = UIApplication.sharedApplication();
var enabledTypes = 0;
if (app.currentUserNotificationSettings) {
var settings = app.currentUserNotificationSettings();
enabledTypes = settings.plusGetAttribute("types");
if (enabledTypes == 0) {
result = 0;
console.log("推送权限没有开启");
} else {
result = 1;
console.log("已经开启推送功能!")
}
plus.ios.deleteObject(settings);
} else {
enabledTypes = app.enabledRemoteNotificationTypes();
if (enabledTypes == 0) {
result = 3;
console.log("推送权限没有开启!");
} else {
result = 4;
console.log("已经开启推送功能!")
}
}
plus.ios.deleteObject(app);
plus.ios.deleteObject(UIApplication);
return result;
}
function contact() {
var result = 0;
var CNContactStore = plus.ios.import("CNContactStore");
var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
if (cnAuthStatus === 0) {
result = null;
} else if (cnAuthStatus == 3) {
result = 1;
} else {
result = 0;
}
plus.ios.deleteObject(CNContactStore);
return result;
}
function record() {
var result = null;
var avaudiosession = plus.ios.import("AVAudioSession");
var avaudio = avaudiosession.sharedInstance();
var status = avaudio.recordPermission();
console.log("permissionStatus:" + status);
if (status === 1970168948) {
result = null;
} else if (status === 1735552628) {
result = 1;
} else {
result = 0;
}
plus.ios.deleteObject(avaudiosession);
return result;
}
function calendar() {
var result = null;
var EKEventStore = plus.ios.import("EKEventStore");
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
if (ekAuthStatus == 3) {
result = 1;
console.log("日历权限已经开启");
} else {
console.log("日历权限没有开启");
}
plus.ios.deleteObject(EKEventStore);
return result;
}
function memo() {
var result = null;
var EKEventStore = plus.ios.import("EKEventStore");
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
if (ekAuthStatus == 3) {
result = 1;
console.log("备忘录权限已经开启");
} else {
console.log("备忘录权限没有开启");
}
plus.ios.deleteObject(EKEventStore);
return result;
}
function requestIOS(permissionID) {
return new Promise((resolve, reject) => {
switch (permissionID) {
case "push":
resolve(push());
break;
case "location":
resolve(location());
break;
case "record":
resolve(record());
break;
case "camera":
resolve(camera());
break;
case "album":
resolve(album());
break;
case "contact":
resolve(contact());
break;
case "calendar":
resolve(calendar());
break;
case "memo":
resolve(memo());
break;
default:
resolve(0);
break;
}
});
}
function requestAndroid(permissionID) {
return new Promise((resolve, reject) => {
plus.android.requestPermissions(
[permissionID],
function(resultObj) {
var result = 0;
for (var i = 0; i < resultObj.granted.length; i++) {
var grantedPermission = resultObj.granted[i];
console.log('已获取的权限:' + grantedPermission);
result = 1
}
for (var i = 0; i < resultObj.deniedPresent.length; i++) {
var deniedPresentPermission = resultObj.deniedPresent[i];
console.log('拒绝本次申请的权限:' + deniedPresentPermission);
result = 0
}
for (var i = 0; i < resultObj.deniedAlways.length; i++) {
var deniedAlwaysPermission = resultObj.deniedAlways[i];
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
result = -1
}
resolve(result);
},
function(error) {
console.log('result error: ' + error.message)
resolve({
code: error.code,
message: error.message
});
}
);
});
}
function gotoAppPermissionSetting() {
if (permission.isIOS) {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
var setting2 = NSURL2.URLWithString("app-settings:");
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
} else {
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass("android.provider.Settings");
var Uri = plus.android.importClass("android.net.Uri");
var mainActivity = plus.android.runtimeMainActivity();
var intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
mainActivity.startActivity(intent);
}
}
const permission = {
get isIOS(){
return typeof isIOS === 'boolean' ? isIOS : (isIOS = uni.getSystemInfoSync().platform === 'ios')
},
requestIOS: requestIOS,
requestAndroid: requestAndroid,
gotoAppSetting: gotoAppPermissionSetting
}
export default permission

142
im-uniapp/common/pinyin.js Normal file

File diff suppressed because one or more lines are too long

1100
im-uniapp/common/publicFc.js Normal file

File diff suppressed because it is too large Load Diff

302
im-uniapp/common/request.js Normal file
View File

@@ -0,0 +1,302 @@
// #ifdef APP-PLUS
import appUpgrade from '@/common/appUpgrade.js';
// #endif
import config from "@/common/config.js"
let http = {
'setBaseUrl': (url) => {
if (url.charAt(url.length - 1) === "/") {
url = url.substr(0, url.length - 1)
}
http.baseUrl = url;
},
'header': {},
'beforeRequestFilter': (config) => {
return config
},
'beforeResponseFilter': (res) => {
return res
},
'afterResponseFilter': (successResult) => {},
'get': get,
'delete': deletE,
'post': post,
'put': put,
'request': request,
'uploadFile': uploadFile,
'downloadFile': downloadFile
}
function init(con) {
//url
let url = http.baseUrl;
if (url && con.url && !con.url.match(/^(http|https):\/\/([\w.]+\/?)\S*$/)) {
if (con.url.charAt(0) !== "/") {
con.url = "/" + con.url;
}
con.url = url.concat(con.url);
}
//header
if (http.header != undefined && http.header != null) {
if (!con.header) {
con.header = http.header;
} else {
Object.keys(http.header).forEach(function(key) {
con.header[key] = http.header[key]
});
}
}
}
function request(con) {
init(con);
let config = { //判断是否有以下属性没有就赋予默认值
url: con.url ? con.url : http.baseUrl,
data: con.data,
header: con.header,
method: con.method ? con.method : 'GET',
dataType: con.dataType ? con.dataType : 'json',
responseType: con.responseType ? con.responseType : 'text',
success: con.success ? (res) => {
http.afterResponseFilter(con.success(http.beforeResponseFilter(res)));
} : null,
fail: con.fail ? (res) => {
con.fail(res);
} : () => {
uni.showToast({
title: '请检查网络',
icon: 'none'
});
uni.hideLoading();
},
complete: con.complete ? (res) => {
con.complete(res);
} : (res) => {
if (!res.statusCode) {
uni.showToast({
title: '请求失败',
icon: 'none'
});
uni.hideLoading();
}
if (res.statusCode !== 200) {
uni.showToast({
title: '请求失败',
icon: 'none'
});
uni.hideLoading();
}
if (res.statusCode == 200) {
if (res.data.code == 401) { //登录过期
// uni.showToast({
// title: '登录已过期,请重新登录',
// icon: 'none'
// });
// uni.hideLoading();
// uni.reLaunch({
// url:'/pages/wxindex/index'
// })
}
// #ifdef APP-PLUS
if (res.data.code == 601) { //强制拉起升级
request({
url: '/common/getVersion',
success: (res) => {
appUpgrade.init({
titleText: '版本更新'+res.data.data.version,
packageUrl:res.data.data.url,
content: res.data.data.content,
forceUpgrade:true
});
appUpgrade.show();
}
});
}
// #endif
else if (res.data.code && res.data.code !== 200) { //这里code是自己的服务器正确标识
uni.showToast({
title: res.data.msg,
icon: 'none',
position:'top'
});
}
uni.hideLoading();
}
uni.stopPullDownRefresh();
}
}
const AUTH_TOKEN = ["Authorization","device","version"];
for (var i = 0; i < AUTH_TOKEN.length; i++) {
if (uni.getStorageSync(AUTH_TOKEN[i])) {
http.header[AUTH_TOKEN[i]] = uni.getStorageSync(AUTH_TOKEN[i]);
}
}
return uni.request(http.beforeRequestFilter(config));
}
function get(url, con, success) {
let conf = {};
if (con && typeof con == 'function') {
if (success && typeof success == 'object') {
conf = success;
}
conf.success = con
} else {
if (con && typeof con == 'object') {
conf = con;
}
conf.success = success;
}
if (url) {
conf.url = url
}
conf.method = "GET";
return request(conf);
}
function deletE(url, con, success) {
let conf = {};
if (con && typeof con == 'function') {
if (success && typeof success == 'object') {
conf = success;
}
conf.success = con
} else {
if (con && typeof con == 'object') {
conf = con;
}
conf.success = success;
}
if (url) {
conf.url = url
}
conf.method = "DELETE";
return request(conf);
}
function post(url, data, con, success) {
let conf = {};
if (con && typeof con == 'function') {
if (success && typeof success == 'object') {
conf = success
}
conf.success = con;
} else {
if (con && typeof con == 'object') {
conf = con;
}
conf.success = success;
}
if (url) {
conf.url = url
}
if (data) {
conf.data = data
}
conf.method = "POST";
return request(conf);
}
function put(url, data, con, success) {
let conf = {};
if (con && typeof con == 'function') {
if (success && typeof success == 'object') {
conf = success
}
conf.success = con;
} else {
if (con && typeof con == 'object') {
conf = con;
}
conf.success = success;
}
if (url) {
conf.url = url
}
if (data) {
conf.data = data
}
conf.method = "PUT";
return request(conf);
}
function uploadFile(con) {
init(con);
console.log("========================")
console.log("========================")
console.log("========================")
console.error(con.url)
console.error(con.url)
console.error(con.url)
console.error(con.url)
let config = {
url: con.url ? con.url : http.baseUrl,
files: con.files,
filesType: con.filesType,
filePath: con.filePath,
name: con.name,
header: con.header,
formData: con.formData,
success: con.success ? (res) => {
http.afterResponseFilter(con.success(http.beforeResponseFilter(res)));
} : null,
fail: con.fail ? (res) => {
con.fail(res);
} : null,
complete: con.complete ? (res) => {
con.complete(res);
} : null
}
const AUTH_TOKEN = ["Authorization","device","version"];
for (var i = 0; i < AUTH_TOKEN.length; i++) {
if (uni.getStorageSync(AUTH_TOKEN[i])) {
http.header[AUTH_TOKEN[i]] = uni.getStorageSync(AUTH_TOKEN[i]);
}
}
return uni.uploadFile(http.beforeRequestFilter(config));
}
function downloadFile(con) {
init(con);
let config = {
url: con.url ? con.url : http.baseUrl,
header: con.header,
success: con.success ? (res) => {
http.afterResponseFilter(con.success(http.beforeResponseFilter(res)));
} : null,
fail: con.fail ? (res) => {
con.fail(res);
} : null,
complete: con.complete ? (res) => {
con.complete(res);
} : null
}
return uni.downloadFile(http.beforeRequestFilter(config));
}
// 使用
//POST
// this.$http.request({
// url: '/xxx',
// method: 'POST',
// data:JSON.stringify({id:111}),
// success: (res) => {
// if (res.data.code == 200) {
//
// }
// }
// });
//GET
// this.$http.request({
// url: '/xxx?id=111',
// success: (res) => {
// if (res.data.code == 200) {
//
// }
// }
// });
// 地址及配置
http.setBaseUrl(config.getBaseApiUrl()); //在线服务器
// http.setBaseUrl("http://192.168.0.105:8080"); //离线服务器
export default http

View File

@@ -0,0 +1,195 @@
import config from "@/common/config.js"
let timer = null
let retimer = null
import store from '../store'
import * as sdk from "@/common/imSdk/lim-sdk.js";
import lim from "@/common/imSdk/sdk-bridge.js";
import publicFc from '@/common/publicFc';
import localForage from 'localforage';
export default {
socketTask: null,
// 连接WebSocket
connectSocket() {
const ListenerMap = {
onSocketConnectEvent: (option, status, data) => {
console.log("已建立连接:" + JSON.stringify(status));
},
onSocketErrorEvent: (error) => {
console.log("连接出现错误:" + userId);
},
onSocketReConnectEvent: () => {
console.log("正在重连:");
},
onSocketCloseEvent: () => {
console.log("连接关闭:");
},
onSocketReConnectSuccessEvent: () => {
console.log("重连成功");
},
onOfflineMessage: (e) => {
// console.log("====================拉取到离线消息")
// console.log(e)
const userId = uni.getStorageSync("userId")
setTimeout(function() {
if (e.length > 0) {
e.map((msg) => {
setTimeout(() => {
const _data = publicFc.formatMessage(msg)
console.log(_data)
if (_data.fromId != userId) {
uni.$emit('onP2PMessage', _data);
store.dispatch('onP2PMessage', _data)
}
}, 500)
})
}
}, 500);
},
onTestMessage: (e) => {
console.log("onTestMessage ");
store.dispatch('onP2PMessage', e)
},
onP2PMessage: (e) => {
console.log("onP2PMessag e :::");
console.log(JSON.parse(e))
console.log("接收到的ws原始数据")
e = JSON.parse(e)
console.log(e)
console.log("接收到的ws格式化之后数据")
const _data = publicFc.formatMessage(e.data)
console.log(_data)
uni.$emit('onP2PMessage', _data);
store.dispatch('onP2PMessage', _data)
},
onLogin: (uid) => {
console.log("用户" + uid + "登陆sdk成功");
}
};
const userId = uni.getStorageSync("userId")
const imUserSign = uni.getStorageSync("imUserSign")
var listeners = {};
for (const v in ListenerMap) {
listeners[v] = ListenerMap[v];
}
console.log(lim)
// 防止多次建立ws链接貌似还没有生效需要检查
if (this.socketTask != null) {
return false
}
lim.lim.im.init(config.getImApiUrl(), config.getImAppId(), userId, imUserSign, listeners, (
sdk) => {
if (sdk) {
console.warn("====================")
this.socketTask = sdk;
console.log(this.socketTask)
console.log('sdk 成功连接的回调, 可以使用 sdk 请求数据了.');
sdk.getSingleUserInfo(userId).then(res => {
console.log("获取到自己的用户信息")
console.log(res)
if (res.code == 200) {
uni.setStorageSync('userInfo', res.data);
}
})
// sdk.getAllFriend().then(res => {
// console.warn(res)
// })
// sdk.getUserSequence().then(res => {
// console.warn(res)
// })
sdk.getUserConversationList(0).then(res => {
console.warn(res)
})
} else {
console.log('sdk 初始化失败.');
}
})
/*
this.socketTask = uni.connectSocket({
url: 'wss://im-api.q3z3.com/ws?Authorization=' + uni.getStorageSync('Authorization'),
complete: () => {}
});
this.socketTask.onOpen(res => {
console.log('WebSocket连接已打开');
// 设置心跳
timer = setInterval(() => {
uni.sendSocketMessage({
data: 'isConnact',
success: res => {
if (res.errMsg == 'sendSocketMessage:ok') {
console.log('WebSocket已连接')
} else {
this.connectSocket()
}
}
})
}, 5000)
})
// 监听接收
this.socketTask.onMessage(res => {
if (res.data == 'ok') {
return
}
var data = JSON.parse(res.data);
fc.getPush(data);
console.log('WebSocket接收消息');
})
// 监听关闭
this.socketTask.onClose(res => {
console.log('WebSocket连接已关闭');
let token = uni.getStorageSync('Authorization');
if (this.socketTask && token) {
this.socketTaskClose()
retimer = setTimeout(() => {
this.connectSocket()
}, 5000)
}
})
// 监听异常
this.socketTask.onError(res => {
console.log('WebSocket连接打开失败正在尝试重新打开');
if (this.socketTask) {
this.socketTaskClose()
retimer = setTimeout(() => {
this.connectSocket()
}, 5000)
}
});
*/
},
// 关闭WebSocket
socketTaskClose() {
if (this.socketTask) {
this.socketTask.close()
clearInterval(timer)
clearTimeout(retimer)
console.log('关闭WebSocket');
}
},
}

56
im-uniapp/common/utils.js Normal file
View File

@@ -0,0 +1,56 @@
export function appInit(ms) {
return new Promise((resolve, reject) => {
var i = 0;
let timer = setInterval(() => {
var im_imUserSign = uni.getStorageSync("im_imUserSign")
var hd_userId = uni.getStorageSync("hd_userid")
if(hd_userId){
uni.setStorageSync("userId",hd_userId)
}else{
console.log("============未获取到hd_userid")
}
var hd_token = localStorage.getItem("hd_token")
i = 100;
if (i < 0) {
reject("error")
}
if (im_imUserSign) {
uni.setStorageSync("imUserSign",im_imUserSign)
uni.setStorageSync("token",hd_token)
if(uni.getStorageSync("userId")!=null&&uni.getStorageSync("imUserSign")!=null&&uni.getStorageSync("token")!=null){
clearInterval(timer)
timer = null
resolve("ok")
}
}else{
uni.setStorageSync("initReady",null)
}
}, ms);
})
}
/**
* getUrlKey 截取地址栏参数
* @param value
* @returns {String}
*/
export function getUrlParam(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
let url = window.location.href
let search = url.split('?')[1];
console.log(search);
if (search) {
var r = search.substr(0).match(reg);
if (r !== null) {
return unescape(r[2]);
}
return null;
} else return null;
}

View File

@@ -0,0 +1,75 @@
// 表单验证
// zmm2113@qq.com
/**
* @property {Object} formData 表单
* @property {Object} rules 验证规则
**/
export default {
error: '',
check: function(formData, rules) {
var formDataKeys = Object.keys(formData)
for (var i = 0; i < formDataKeys.length; i++) {
var key = formDataKeys[i]
if (rules[key]) {
var itemRules = rules[key].rules
var itemValue = formData[key]
for (var j = 0; j < itemRules.length; j++) {
var rule = itemRules[j]
switch (rule.checkType){
case 'required'://必填项
if(!itemValue) {this.error = rule.errorMsg; return false;}
break;
case 'phone'://手机号码验证
var reg = /^1[0-9]{10,10}$/;
if (!reg.test(itemValue)) { this.error = rule.errorMsg; return false; }
break;
case 'email'://邮箱验证
var reg = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/;
if (!reg.test(itemValue)) { this.error = rule.errorMsg; return false; }
break;
case 'idcard'://15-18位身份证验证
var reg = /^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/;
if (!reg.test(itemValue)) { this.error = rule.errorMsg; return false; }
break;
case 'url'://网址验证
var reg = /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?$/;
if (!reg.test(itemValue)) { this.error = rule.errorMsg; return false; }
break;
case 'reg'://正则验证 checkRule必填
if(!rule.checkRule){
this.error=key+' 校验失败'
console.warn(key+' checkRule规则未填写')
return false;
}
var reg = rule.checkRule;
if (!reg.test(itemValue)) { this.error = rule.errorMsg; return false; }
break;
case 'number'://数字
var reg = /^\d+$/;
if (!reg.test(itemValue)) { this.error = rule.errorMsg; return false; }
break;
case 'same'://是否相同 checkRule必填
if(!rule.checkRule){
this.error=key+' 校验失败'
console.warn(key+' checkRule规则未填写')
return false;
}
if (itemValue!==formData[rule.checkRule]) { this.error = rule.errorMsg; return false; }
break;
case 'string'://字符串 checkRule必填
if(!rule.checkRule){
this.error=key+' 校验失败'
console.warn(key+' checkRule规则未填写')
return false;
}
var reg = new RegExp('^.{' + rule.checkRule + '}$');
if (!reg.test(itemValue)) { this.error = rule.errorMsg; return false; }
break;
}
}
}
}
return true;
}
}

View File

@@ -0,0 +1,620 @@
<template>
<view class="friendsCircle-content-item">
<image class="friendsCircle-content-avatar" :src="content.avatar" mode="aspectFill"></image>
<view class="friendsCircle-content-right">
<view class="friendsCircle-content-nikeName">{{ content.nikeName }}</view>
<view class="friendsCircle-content-text">{{ content.text }}</view>
<view class="friendsCircle-content-imgs" v-if="content.topicType=='IMAGE'&&content.files.length>0">
<image v-for="(item, ii) in content.files" :key="ii" class="friendsCircle-content-img" :class="'fci' + content.files.length" :src="item.url" mode="aspectFill" @click="pimgs(content.files,ii)"></image>
</view>
<view class="friendsCircle-content-video" @click="openVideo(content.files[0].videoUrl)" v-if="content.topicType=='VIDEO'&&content.files.length>0">
<image class="friendsCircle-content-video-icon" src="../../static/img/bf.png" mode="aspectFill"></image>
<image class="friendsCircle-content-video-img" :src="content.files[0].url" mode="aspectFill"></image>
</view>
<view v-if="content.location" class="friendsCircle-content-location" @click="goMap(content.location)">{{ content.location.name }}</view>
<view class="friendsCircle-content-tools">
<view class="friendsCircle-content-time">{{ content.time }}</view>
<view class="friendsCircle-content-tool" :class="{ 'tabon': tabToolIndex }">
<view class="wxfont caidan" @click="tabTool(i)"></view>
<view class="friendsCircle-content-tool-absolute">
<view class="friendsCircle-popup">
<view class="friendsCircle-popup-item" @click="zan(content)">
<view class="friendsCircle-popup-icon wxfont zan"></view>
<view class="friendsCircle-popup-text" v-if="content.like=='Y'">取消</view>
<view class="friendsCircle-popup-text" v-else></view>
</view>
<view class="friendsCircle-popup-item" @click="comment(content)">
<view class="friendsCircle-popup-icon wxfont pinglun"></view>
<view class="friendsCircle-popup-text">评论</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="friendsCircle-content-item-detail">
<view class="friendsCircle-content-item-detail-item" v-if="content.likeList&&content.likeList.length>0">
<view class="friendsCircle-like-icon wxfont zan"></view>
<view class="friendsCircle-like">
<image class="friendsCircle-like-img" mode="aspectFill" v-for="(z,zindex) in content.likeList" :src="z.portrait"></image>
</view>
</view>
<view class="friendsCircle-content-item-detail-item" v-if="content.replyList&&content.replyList.length>0">
<view class="friendsCircle-comment-icon wxfont pinglun"></view>
<view class="friendsCircle-comment">
<view class="friendsCircle-comment-item" v-for="(c,ci) in content.replyList" :key="ci">
<image class="friendsCircle-comment-img" :src="c.portrait" mode="aspectFill"></image>
<view class="friendsCircle-comment-item-text">
<view class="friendsCircle-comment-item-name">
<text class="friendsCircle-comment-text">{{c.nickName}}</text>
<text class="friendsCircle-comment-item-time">{{c.createTime}}</text>
</view>
<view style="width: 100%;" @click="replyclick(c,ci)">
<view class="friendsCircle-comment-feedback" v-if="c.replyType=='2'">
<text>回复</text>
<text class="friendsCircle-comment-text">{{c.toNickName}}</text>
</view>
<text class="friendsCircle-comment-textx">{{c.content}}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view style="height: 120rpx;"></view>
<view class="zfb-tk-send-tool" :style="{bottom:keyboardHeight+'px'}">
<form @submit="sendMsg(msg)">
<view class="zfb-tk-send-tool-c">
<view class="zfb-tk-send-tool-input-box" @click="msgFocus=true">
<textarea class="zfb-tk-send-tool-input" :adjust-position="false" :focus="msgFocus" v-model="msg" :placeholder="placeholder" confirm-type="send" @confirm="sendMsg(msg)" hold-keyboard :maxlength="-1" auto-height />
</view>
<view class="zfb-tk-send-tool-text" @click="sendMsg(msg)" :style="{ background: msg !== '' ? '#1BC418' : '#F7F7F7', color: msg !== '' ? '#fff' : '#ddd', 'border-color': msg !== '' ? '#1BC418' : '#ddd' }">发送</view>
</view>
</form>
</view>
</template>
<script>
export default {
// 朋友圈列表内容区
data() {
return {
timer:null,
placeholder:'评论',
keyboardHeight:0,
msg: '',
type:'msg',
replydata:'',
msgFocus:false,
tabToolIndex: false,
content:this.detail
};
},
props:{
detail:{
type:Object
}
},
watch:{
detail(v){
this.content=v
}
},
computed:{
userInfo(){
return this.$store.state.userInfo
}
},
mounted() {
// #ifdef APP-PLUS
uni.onKeyboardHeightChange(res => {
this.keyboardHeight=res.height
})
// #endif
},
methods: {
scrolltoBottom() {
this.$nextTick(()=>{
this.timer = setTimeout(() => {
uni.pageScrollTo({
scrollTop: 9999999,
duration: 10
});
this.$forceUpdate()
}, 100);
})
},
replyclick(e,ci){
if(e.canDeleted=='Y'){
this.hidenTool()
uni.showActionSheet({
itemList: ['复制','删除'],
success: (res) => {
switch (res.tapIndex){
case 0:
uni.setClipboardData({
data: e.content,
success: function() {
uni.showToast({
title: '复制成功',
icon: 'none',
position: 'bottom'
})
}
});
break;
case 1:
this.$http.request({
url: '/topic/removeReply/'+e.replyId,
success: (res) => {
if (res.data.code == 200) {
uni.showToast({
title:'删除成功',
icon:'none'
})
this.content.replyList.splice(ci,1)
}
}
});
break;
default:
break;
}
}
});
}else{
this.replydata=e
this.type='reply'
this.placeholder='回复'+e.nickName
this.$nextTick(()=>{
this.msgFocus=true
})
}
},
gofriend(e){
uni.navigateTo({
url:'../../wx/personInfo/detail?param='+e.userId
})
},
pimgs(arr,ii){
var imgs=[]
for (var i = 0; i < arr.length; i++) {
imgs.push(arr[i].url)
}
this.$fc.previewImages(imgs, ii)
},
goMap(e) {
uni.openLocation({
latitude: e.latitude,
longitude: e.longitude,
success: function() {}
});
},
openVideo(e){
this.$fc.plusDownload({onlinePath:e}).then(res=>{
this.$fc.plusOpenFile({filePath:res})
})
},
tabTool(e) {
this.tabToolIndex=!this.tabToolIndex
},
comment(e) {
this.hidenTool()
//评论
this.type='msg'
this.placeholder='评论'
this.$nextTick(()=>{
this.msgFocus=true
})
},
zan(e,i){//点赞
switch (e.like){
case 'N':
this.$http.request({
url: '/topic/like/'+e.topicId,
success: (res) => {
if (res.data.code == 200) {
var like=e.like=='N' ? e.like='Y' : e.like='N'
this.content.like=like
this.content.likeList.push({
userId: this.userInfo.userId,
nickName: this.userInfo.nickName,
portrait: this.userInfo.portrait
})
}
}
});
break;
case 'Y':
this.$http.request({
url: '/topic/cancelLike/'+e.topicId,
success: (res) => {
if (res.data.code == 200) {
var like=e.like=='N' ? e.like='Y' : e.like='N'
this.content.like=like
var thisObj=this.$fc.arrFindkey({arr:this.content.likeList,key:'userId',val:this.userInfo.userId})
this.content.likeList.splice(thisObj.index,1)
}
}
});
break;
default:
break;
}
},
sendMsg(e) {
if (!e) {
return;
}
var replyId=''
var replyType=''
if(this.type=='msg'){
replyId=this.content.topicId
replyType='1'
}
if(this.type=='reply'){
replyId=this.replydata.replyId
replyType='2'
}
this.$http.request({
url: '/topic/reply',
method: 'POST',
data:JSON.stringify({
replyId: replyId,
replyType: replyType,
content: e
}),
success: (res) => {
if (res.data.code == 200) {
this.content.replyList.push(res.data.data)
this.$nextTick(()=>{
this.scrolltoBottom()
})
}
}
});
this.msg=''
},
hidenTool() {//隐藏选项和输入框
this.tabToolIndex = false;
}
}
};
</script>
<style scoped lang="scss">
.zfb-tk-send-tool {
background: #f7f7f7;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
}
.zfb-tk-send-tool-c {
padding: 16rpx 12rpx;
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
border: 1px #ddd solid;
border-left: none;
border-right: none;
}
.zfb-tk-send-tool-c .zfb-tk-send-tool-btn {
transition: color 0.5s;
}
.zfb-tk-send-tool .zfb-tk-send-tool-input {
padding:0 24rpx;
box-sizing: border-box !important;
width: 100%;
background: #fff;
}
.zfb-tk-send-tool-text {
white-space: nowrap;
padding: 10rpx 24rpx;
border-radius: 12rpx;
border: 1px #ddd solid;
background: #f7f7f7;
color: #ddd;
}
.friendsCircle-content-item{
display: flex;
flex-direction: row;
padding:24rpx;
}
.friendsCircle-content-item-detail{
margin:0 24rpx;
border-radius: 8rpx;
margin-top: 24rpx;
padding-bottom: 0;
margin-bottom: 1px;
}
.friendsCircle-content-avatar{
border-radius: 10rpx;
width: 90rpx;
height: 90rpx;
min-width: 90rpx;
}
.friendsCircle-content-right{
display: flex;flex-direction: column;
padding-left: 18rpx;
flex: 1;
box-sizing: border-box;
}
.friendsCircle-content-nikeName{
font-size: 32rpx;
color: #5F698C;
margin-bottom: 10rpx;
}
.friendsCircle-content-text{
font-size: 32rpx;
color: #333;
}
.friendsCircle-content-imgs{
display: flex;
flex-direction: row;
flex-wrap: wrap;
flex: 1;
margin-top: 24rpx;
}
.friendsCircle-content-img{
max-width: 100%;
}
.friendsCircle-content-img.fci1{
height: 550rpx;
max-width: 500rpx;
margin-right: 5px;
}
.friendsCircle-content-img.fci2{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci2:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci3{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci3:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci4{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci4:nth-child(2n){
margin-right: 185rpx;
}
.friendsCircle-content-img.fci5{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci5:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci6{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci6:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci7{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci7:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci8{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci8:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci9{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci9:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-location{
font-size: 24rpx;
color: #5F698C;
margin-top: 12rpx;
}
.friendsCircle-content-time{
font-size: 24rpx;
color: #666666;
margin-right: auto;
}
.friendsCircle-content-tools{
margin-top: 24rpx;
display: flex;flex-direction: row;align-items: center;
}
.friendsCircle-content-tool{
padding:4rpx 16rpx;
border-radius: 10rpx;
background-color: #F8F8FA;
color: #63678C;
display: flex;flex-direction: row;
position: relative;
}
.tabon .friendsCircle-content-tool-absolute{
display: block;
}
.friendsCircle-content-tool-absolute{
position: absolute;
right: 100%;
top: 50%;
transform: translateY(-50%);
display: none;
}
.friendsCircle-popup{
margin-right: 24rpx;
border-radius: 12rpx;
background-color: rgba(0,0,0,0.7);
height: 75rpx;
display: flex;flex-direction: row;align-items: center;
padding:0 24rpx;
}
.friendsCircle-popup-item{
color: #fff;
padding:0 25rpx;
height: 100%;
display: flex;flex-direction: row;align-items: center;
}
.friendsCircle-popup-item:nth-child(1):active .friendsCircle-popup-icon{
font-size: 56rpx;
}
.friendsCircle-popup-icon{
font-size: 46rpx;
}
.friendsCircle-popup-text{
white-space: nowrap;
}
.friendsCircle-like{
display: flex;flex-direction: row;align-items: center;flex-wrap: wrap;
}
.friendsCircle-like-icon,.friendsCircle-like-text,.friendsCircle-comment-text{
color: #5F698C !important;
font-size: 24rpx;
}
.friendsCircle-comment-feedback .friendsCircle-comment-text{
margin:0 6rpx;
}
.friendsCircle-comment-feedback .friendsCircle-comment-text::after{
content: ":";
color: #333;
}
.friendsCircle-comment-item-name{
display: flex;flex-direction: row;
width: 100%;
}
.friendsCircle-comment-item-time{
margin-left: auto;
font-size: 24rpx;
color: #999;
}
.friendsCircle-like-icon,.friendsCircle-comment-icon{
width: 40rpx;
min-width: 40rpx;
height: 60rpx;
display: flex;flex-direction: row;align-items: center;justify-content: center;
font-size: 32rpx;
}
.friendsCircle-like-text{
margin-left: 4rpx;
}
.friendsCircle-like-text::after{
content: ",";
}
.friendsCircle-like-text:nth-last-child(1)::after{
content: "";
}
.friendsCircle-comment{
background-color: #F7F7F7;
border-radius: 8rpx;
flex: 1;
}
.friendsCircle-comment-item{
font-size: 28rpx;
padding: 12rpx;
white-space: pre-wrap;
display: flex;flex-direction: row;
}
.friendsCircle-comment-feedback{
display: inline-block;
}
.friendsCircle-comment-feedback text{
font-size: 28rpx;
color: #333;
}
.friendsCircle-comment-textx{
word-break: break-all;
}
.friendsCircle-like-img,.friendsCircle-comment-img{
width: 60rpx;
height: 60rpx;
border-radius: 12rpx;
margin-left: 10rpx;
margin-bottom: 12rpx;
}
.friendsCircle-comment-img{
margin-right: 12rpx;
margin-left: 0px;
}
.friendsCircle-content-item-detail-item{
display: flex;flex-direction: row;
padding: 12rpx;
padding-bottom: 0;
margin-bottom: 4rpx;
border-radius: 8rpx;
background-color: #F7F7F7;
}
.friendsCircle-comment-item-text{
flex: 1;
display: flex;flex-direction: column;
align-items:flex-start;
}
.friendsCircle-content-video{
height: 550rpx;
max-width: 400rpx;
position: relative;
margin-right: 14rpx;
margin-top: 24rpx;
}
.friendsCircle-content-video-img{
width: 100%;
height: 100%;
}
.friendsCircle-content-video-icon{
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 60rpx !important;
height: 60rpx !important;
transform: translate(-50%,-50%);
}
.zfb-tk-send-tool-input-box{
overflow: auto;
width: 100%;
margin: 0 12rpx;
min-height: 75rpx;
background-color: #fff;
border-radius: 24rpx;
padding-top: 18rpx;
max-height: 225rpx;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,558 @@
<template>
<view class="friendsCircle-content-item" v-for="(v, i) in content" :key="i">
<image class="friendsCircle-content-avatar" :src="v.avatar" mode="aspectFill" @click="gofriend(v)"></image>
<view class="friendsCircle-content-right">
<view class="friendsCircle-content-nickName" @click="gofriend(v)">{{ v.nickName }}</view>
<view class="friendsCircle-content-text">{{ v.text }}</view>
<view class="friendsCircle-content-imgs" v-if="v.topicType=='IMAGE'&&v.files.length>0">
<image v-for="(item, ii) in v.files" :key="ii" class="friendsCircle-content-img" :class="'fci' + v.files.length" :src="item.url" mode="aspectFill" @click="pimgs(v.files,ii)"></image>
</view>
<view class="friendsCircle-content-video" @click="openVideo(v.files[0].videoUrl)" v-if="v.topicType=='VIDEO'&&v.files.length>0">
<image class="friendsCircle-content-video-icon" src="../../static/img/bf.png" mode="aspectFill"></image>
<image class="friendsCircle-content-video-img" :src="v.files[0].url" mode="aspectFill"></image>
</view>
<view class="friendsCircle-content-location" v-if="v.location.name" @click="goMap(v.location)">{{ v.location.name }}</view>
<view class="friendsCircle-content-tools">
<view class="friendsCircle-content-time">{{ v.time }}</view>
<view class="friendsCircle-content-tool" :class="{ tabon: i == showToolIndex }">
<view class="wxfont caidan" @click="showTool(i)"></view>
<view class="friendsCircle-content-tool-absolute">
<view class="friendsCircle-popup">
<view class="friendsCircle-popup-item" @click="zan(v,i)">
<view class="friendsCircle-popup-icon wxfont zan"></view>
<view class="friendsCircle-popup-text" v-if="v.like=='Y'">取消</view>
<view class="friendsCircle-popup-text" v-else></view>
</view>
<view class="friendsCircle-popup-item" @click="comment(v)">
<view class="friendsCircle-popup-icon wxfont pinglun"></view>
<view class="friendsCircle-popup-text">评论</view>
</view>
</view>
</view>
</view>
</view>
<view class="friendsCircle-like" v-if="v.likeList&&v.likeList.length>0">
<view class="friendsCircle-like-icon wxfont zan"></view>
<text class="friendsCircle-like-text" v-for="(z,zindex) in v.likeList">{{z.nickName}}</text>
</view>
<view class="friendsCircle-comment" v-if="v.replyList&&v.replyList.length>0">
<view class="friendsCircle-comment-item" v-for="(c,ci) in v.replyList" :key="ci">
<text class="friendsCircle-comment-text" @click="gofriend(c)">{{c.nickName}}</text>
<view class="friendsCircle-comment-feedback" v-if="c.replyType=='2'">
回复
<text class="friendsCircle-comment-text" @click="gofriend(c)">{{c.toNickName}}</text>
</view>
<view class="friendsCircle-comment-content" v-if="c.content" @click="replyclick(c,ci,i)">
<text>{{c.content}}</text>
</view>
</view>
</view>
</view>
</view>
<view class="zfb-tk-send-tool" v-if="toggleMsgBox" :style="{bottom:keyboardHeight+'px'}">
<form @submit="sendMsg(msg)">
<view class="zfb-tk-send-tool-c">
<view class="zfb-tk-send-tool-input-box" @click="msgFocus=true">
<textarea class="zfb-tk-send-tool-input" :adjust-position="false" :focus="msgFocus" v-model="msg" :placeholder="placeholder" confirm-type="send" @confirm="sendMsg(msg)" hold-keyboard :maxlength="-1" auto-height />
</view>
<view class="zfb-tk-send-tool-text" @click="sendMsg(msg)" :style="{ background: msg !== '' ? '#1BC418' : '#F7F7F7', color: msg !== '' ? '#fff' : '#ddd', 'border-color': msg !== '' ? '#1BC418' : '#ddd' }">发送</view>
</view>
</form>
</view>
</template>
<script>
export default {
// 朋友圈列表内容区
data() {
return {
keyboardHeight:0,
msg: '',
msgFocus:false,
toggleMsgBox: false,
showToolIndex: null,
chooseUserIndex:null,
type:'msg',
placeholder:'评论',
replydata:''
};
},
props:{
content:{
type:Array,
default:[]
}
},
computed:{
userInfo(){
return this.$store.state.userInfo
}
},
mounted() {
// #ifdef APP-PLUS
uni.onKeyboardHeightChange(res => {
this.keyboardHeight=res.height
})
// #endif
},
methods: {
replyclick(e,ci,i){
this.chooseUserIndex=i
if(e.canDeleted=='Y'){
this.hidenTool()
uni.showActionSheet({
itemList: ['复制','删除'],
success: (res) => {
switch (res.tapIndex){
case 0:
uni.setClipboardData({
data: e.content,
success: function() {
uni.showToast({
title: '复制成功',
icon: 'none',
position: 'bottom'
})
}
});
break;
case 1:
this.$http.request({
url: '/topic/removeReply/'+e.replyId,
success: (res) => {
if (res.data.code == 200) {
uni.showToast({
title:'删除成功',
icon:'none'
})
this.content[i].replyList.splice(ci,1)
}
}
});
break;
default:
break;
}
}
});
}else{
this.replydata=e
this.type='reply'
this.placeholder='回复'+e.nickName
this.$nextTick(()=>{
this.toggleMsgBox = true;
this.msgFocus=true
})
}
},
gofriend(e){
uni.navigateTo({
url:'../../wx/personInfo/detail?param='+e.userId
})
},
pimgs(arr,ii){
var imgs=[]
for (var i = 0; i < arr.length; i++) {
imgs.push(arr[i].url)
}
this.$fc.previewImages(imgs, ii)
},
goMap(e) {
uni.openLocation({
latitude: e.latitude,
longitude: e.longitude,
success: function() {}
});
},
openVideo(e){
this.$fc.plusDownload({onlinePath:e}).then(res=>{
this.$fc.plusOpenFile({filePath:res})
})
},
returnParse(txt) {
return JSON.parse(txt);
},
showTool(e) {
this.chooseUserIndex=e
if (this.showToolIndex === e) {
this.showToolIndex = null;
} else {
this.showToolIndex = e;
}
},
comment(e) {
//评论
this.showToolIndex = null;
this.type='msg'
this.placeholder='评论'
this.$nextTick(()=>{
this.toggleMsgBox = true;
this.msgFocus=true
})
},
zan(e,i){//点赞
switch (e.like){
case 'N':
this.$http.request({
url: '/topic/like/'+e.topicId,
success: (res) => {
if (res.data.code == 200) {
this.hidenTool()
var like=e.like=='N' ? e.like='Y' : e.like='N'
this.content[this.chooseUserIndex].like=like
this.content[this.chooseUserIndex].likeList.push({
userId: this.userInfo.userId,
nickName: this.userInfo.nickName,
portrait: this.userInfo.portrait
})
}
}
});
break;
case 'Y':
this.$http.request({
url: '/topic/cancelLike/'+e.topicId,
success: (res) => {
if (res.data.code == 200) {
this.hidenTool()
var like=e.like=='N' ? e.like='Y' : e.like='N'
this.content[this.chooseUserIndex].like=like
var thisObj=this.$fc.arrFindkey({arr:this.content[this.chooseUserIndex].likeList,key:'userId',val:this.userInfo.userId})
this.content[this.chooseUserIndex].likeList.splice(thisObj.index,1)
}
}
});
break;
default:
break;
}
},
sendMsg(e) {
if (!e) {
return;
}
var replyId=''
var replyType=''
if(this.type=='msg'){
replyId=this.content[this.chooseUserIndex].topicId
replyType='1'
}
if(this.type=='reply'){
replyId=this.replydata.replyId
replyType='2'
}
this.$http.request({
url: '/topic/reply',
method: 'POST',
data:JSON.stringify({
replyId: replyId,
replyType: replyType,
content: e
}),
success: (res) => {
if (res.data.code == 200) {
this.hidenTool()
this.content[this.chooseUserIndex].replyList.push(res.data.data)
}
}
});
this.msg=''
},
hidenTool() {//隐藏选项和输入框
this.showToolIndex = null;
this.toggleMsgBox = false;
}
}
};
</script>
<style scoped lang="scss">
.zfb-tk-send-tool {
background: #f7f7f7;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
}
.zfb-tk-send-tool-c {
padding: 16rpx 12rpx;
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
border: 1px #ddd solid;
border-left: none;
border-right: none;
}
.zfb-tk-send-tool-c .zfb-tk-send-tool-btn {
transition: color 0.5s;
}
.zfb-tk-send-tool .zfb-tk-send-tool-input {
padding:0 24rpx;
box-sizing: border-box !important;
width: 100%;
background: #fff;
}
.zfb-tk-send-tool-text {
white-space: nowrap;
padding: 10rpx 24rpx;
border-radius: 12rpx;
border: 1px #ddd solid;
background: #f7f7f7;
color: #ddd;
}
.friendsCircle-content-item{
display: flex;flex-direction: row;
padding:24rpx 34rpx;
border-bottom: 1px #F2F2F2 solid;
}
.friendsCircle-content-avatar{
border-radius: 10rpx;
width: 90rpx;
height: 90rpx;
min-width: 90rpx;
}
.friendsCircle-content-right{
display: flex;flex-direction: column;
padding-left: 18rpx;
flex: 1;
box-sizing: border-box;
}
.friendsCircle-content-nickName{
font-size: 32rpx;
color: #5F698C;
margin-bottom: 10rpx;
}
.friendsCircle-content-text{
font-size: 32rpx;
color: #333;
}
.friendsCircle-content-imgs{
display: flex;
flex-direction: row;
flex-wrap: wrap;
flex: 1;
margin-top: 24rpx;
}
.friendsCircle-content-img{
max-width: 100%;
}
.friendsCircle-content-img.fci1{
height: 550rpx;
max-width: 500rpx;
margin-right: 5px;
}
.friendsCircle-content-img.fci2{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci2:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci3{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci3:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci4{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci4:nth-child(2n){
margin-right: 185rpx;
}
.friendsCircle-content-img.fci5{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci5:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci6{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci6:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci7{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci7:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci8{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci8:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci9{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci9:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-location{
font-size: 24rpx;
color: #5F698C;
margin-top: 12rpx;
}
.friendsCircle-content-time{
font-size: 24rpx;
color: #666666;
margin-right: auto;
}
.friendsCircle-content-tools{
margin-top: 24rpx;
display: flex;flex-direction: row;align-items: center;
margin-bottom: 14rpx;
}
.friendsCircle-content-tool{
padding:4rpx 16rpx;
border-radius: 10rpx;
background-color: #F8F8FA;
color: #63678C;
display: flex;flex-direction: row;
position: relative;
}
.tabon .friendsCircle-content-tool-absolute{
display: block;
}
.friendsCircle-content-tool-absolute{
position: absolute;
right: 100%;
top: 50%;
transform: translateY(-50%);
display: none;
}
.friendsCircle-popup{
margin-right: 24rpx;
border-radius: 12rpx;
background-color: rgba(0,0,0,0.7);
height: 75rpx;
display: flex;flex-direction: row;align-items: center;
padding:0 24rpx;
}
.friendsCircle-popup-item{
color: #fff;
padding:0 25rpx;
height: 100%;
display: flex;flex-direction: row;align-items: center;
}
.friendsCircle-popup-item:nth-child(1):active .friendsCircle-popup-icon{
font-size: 56rpx;
}
.friendsCircle-popup-icon{
font-size: 46rpx;
}
.friendsCircle-popup-text{
white-space: nowrap;
}
.friendsCircle-like{
border-radius: 8rpx;
padding: 12rpx;
background-color: #F7F7F7;
display: flex;flex-direction: row;align-items: center;flex-wrap: wrap;
margin-bottom: 1px;
}
.friendsCircle-like-icon,.friendsCircle-like-text,.friendsCircle-comment-text{
color: #5F698C !important;
font-size: 28rpx;
}
.friendsCircle-comment-feedback .friendsCircle-comment-text{
margin:0 6rpx;
}
.friendsCircle-comment-text::after{
content: ":";
color: #333;
}
.friendsCircle-like-text{
margin-left: 4rpx;
margin-right: 10rpx;
}
.friendsCircle-like-text::after{
content: ",";
}
.friendsCircle-like-text:nth-last-child(1)::after{
content: "";
}
.friendsCircle-comment{
background-color: #F7F7F7;
border-radius: 8rpx;
}
.friendsCircle-comment-item{
line-height: 40rpx;
font-size: 28rpx;
padding: 12rpx;
white-space: pre-wrap;
display: flex;flex-direction: row;flex-wrap: wrap;
}
.friendsCircle-comment-feedback{
display: inline-block;
}
.friendsCircle-content-video{
width: auto;
height: 550rpx;
max-width: 400rpx;
position: relative;
margin-top: 24rpx;
}
.friendsCircle-content-video-img{
width: 100%;
height: 100%;
}
.friendsCircle-content-video-icon{
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 80rpx !important;
height: 80rpx !important;
transform: translate(-50%,-50%);
}
.friendsCircle-comment-content{
}
.friendsCircle-comment-content text{
word-break: break-all;
}
.zfb-tk-send-tool-input-box{
overflow: auto;
width: 100%;
margin: 0 12rpx;
min-height: 75rpx;
background-color: #fff;
border-radius: 24rpx;
padding-top: 18rpx;
max-height: 225rpx;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,225 @@
<template>
<view class="friends-circle-person-item" v-for="(v, i) in content" :key="i">
<view class="friends-circle-person-item-date"><!-- -->
<text class="day">{{returnDate(v.createTime,'d')}}</text>
<text class="month">{{returnDate(v.createTime,'m')}}</text>
<view class="year">{{returnDate(v.createTime,'y')}}</view>
<view class="year" v-if="v.location.name">{{v.location.name}}</view>
</view>
<view class="friends-circle-person-view" @click="clickItem(v)">
<view class="friends-circle-person-content" v-if="v.topicType=='IMAGE'&&v.files.length>0">
<view class="friends-circle-person-imgs">
<image v-for="(item, ii) in v.files.slice(0,4)" :key="ii" :class="'fci' + v.files.slice(0,4).length" :src="item.url" mode="aspectFill"></image>
</view>
<view class="friends-circle-person-imgs-text">
<text>{{v.text}}</text>
<view class="friends-circle-person-imgs-total">{{v.files.length}}</view>
</view>
</view>
<view class="friends-circle-person-content" v-if="v.topicType=='VIDEO'&&v.files.length>0">
<view class="friendsCircle-content-video">
<image class="friendsCircle-content-video-icon" src="../../static/img/bf.png" mode="aspectFill"></image>
<image class="friendsCircle-content-video-img" :src="v.files[0].url" mode="aspectFill"></image>
</view>
<view class="friends-circle-person-imgs-text">
<text>{{v.text}}</text>
</view>
</view>
<view v-if="v.topicType=='TEXT'" class="friends-circle-person-content">
<view class="friends-circle-person-text">
<text>{{v.text}}</text>
</view>
</view>
</view>
</view>
<view class="friends-circle-nodata" v-if="content.length<=0">
<view class="friends-circle-nodata-line"></view>
<view class="friends-circle-nodata-text">未发布朋友圈</view>
</view>
</template>
<script>
export default {
// 朋友圈单人列表
emits:['clickItem'],
data() {
return {
};
},
props:{
content:{
type:Array,
default:[]
}
},
methods: {
returnDate(e,format){
return this.$fc.getNewDate(format,true,e)
},
clickItem(e){
this.$emit('clickItem',e)
}
}
};
</script>
<style scoped lang="scss">
.friends-circle-person-item{
display: flex;flex-direction: row;
margin-bottom: 60rpx;
width: 100%;
}
.friends-circle-person-view{
flex: 1;
}
.friends-circle-person-item-date{
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-right: 40rpx;
width: 115rpx;
min-width: 115rpx;
line-height: 60rpx;
text-align: right;
}
.friends-circle-person-item-date .day{
flex: 1;
font-size: 43rpx;
}
.friends-circle-person-item-date .month{
font-size: 26rpx;
flex: 1;
}
.friends-circle-person-item-date .year{
width: 100%;
min-width: 100%;
font-size: 24rpx;
color: #999;
line-height: initial;
}
.friends-circle-person-content{
flex: 1;
display: flex;flex-direction: row;
}
.friends-circle-person-text{
padding: 12rpx;
background-color: #eee;
border-radius: 4rpx;
}
.friends-circle-person-text text{
font-size: 28rpx;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.friends-circle-person-imgs{
width: 140rpx;
min-width: 140rpx;
height: 140rpx;
margin-right: 14rpx;
}
.friends-circle-person-imgs image{float: left;}
.friends-circle-person-imgs .fci1{
width: 100%;
height: 100%;
}
.friends-circle-person-imgs .fci2{
margin-right: 2%;
width: 49%;
height: 100%;
}
.friends-circle-person-imgs .fci2:nth-child(2n){
margin-right: 0;
}
.friends-circle-person-imgs .fci3{
margin-right: 2%;
width: 49%;
height: 48%;
}
.friends-circle-person-imgs .fci3:nth-child(1){
height: 98%;
}
.friends-circle-person-imgs .fci3:nth-child(2){
margin-bottom: 2%;
}
.friends-circle-person-imgs .fci3:nth-child(2),.friends-circle-person-imgs .fci3:nth-child(3){
margin-right: 0;
}
.friends-circle-person-imgs .fci4{
margin-right: 2%;
width: 49%;
height: 49%;
margin-bottom: 2%;
}
.friends-circle-person-imgs .fci4:nth-child(2n){
margin-right: 0;
}
.friends-circle-person-imgs-text{
display: flex;
flex-direction: column;
justify-content: space-between;
}
.friends-circle-person-imgs-text text{
line-height: 1.2;
font-size: 28rpx;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
.friends-circle-person-imgs-total{
color: #999;
font-size: 24rpx;
}
.friends-circle-nodata{
display: flex;flex-direction: column;
}
.friends-circle-nodata-line{
margin: 50rpx;
margin-bottom: 30rpx;
height: 1px;
position: relative;
background-color: #eee;
}
.friends-circle-nodata-line::after{
content: " ";
width: 13rpx;
height: 13rpx;
border-radius: 50%;
background-color: #eee;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.friends-circle-nodata-text{
font-size: 24rpx;
text-align: center;
color: #999;
}
.friendsCircle-content-video{
width: 140rpx;
height: 140rpx;
position: relative;
margin-right: 14rpx;
}
.friendsCircle-content-video-img{
width: 100%;
height: 100%;
}
.friendsCircle-content-video-icon{
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 60rpx !important;
height: 60rpx !important;
transform: translate(-50%,-50%);
}
</style>

View File

@@ -0,0 +1,327 @@
<template>
<view class="friendsCircle-top" :class="{ 'friendsCircle-top-opend': CircleTop, 'friendsCircle-top-type-img': cover.type == 'img' }" :key="ikey">
<view style="width: 100%;height: 100%;" v-if="cover.type == 'img'">
<image class="friendsCircle-top-img" @click="changeCircleTop" :src="cover.url" mode="aspectFill"></image>
<view class="friendsCircle-top-post" @click="changePoster" v-if="showChangePoster">
<view class="wxfont tupian"></view>
<text class="text">换封面</text>
</view>
<view class="friendsCircle-top-information" @click="userClick" v-if="userInfo">
<view class="friendsCircle-top-information-user">
<view class="friendsCircle-top-information-nikeName">{{userInfo.nickName}}</view>
<image class="friendsCircle-top-information-avatar" :src="userInfo.portrait" mode="aspectFill"></image>
</view>
<view class="friendsCircle-top-information-signature">{{userInfo.intro}}</view>
</view>
</view>
<video v-else class="friendsCircle-top-video" id="videodemo" ref="videodemo" :key="vkey" :autoplay="false" :src="cover.url" :controls="false" :loop="true" :show-center-play-btn="false" object-fit="cover" muted>
<cover-view class="friendsCircle-top-video-model-img-box"><cover-image class="friendsCircle-top-video-model-img" :src="cover.screenShot"></cover-image></cover-view>
<cover-view class="friendsCircle-top-video-model" @click="changeCircleTop"></cover-view>
<cover-view class="friendsCircle-top-post" @click="changePoster" v-if="showChangePoster">
<cover-image @click="changePoster" class="friendsCircle-top-post-img" src="../../static/img/f01.png"></cover-image>
<cover-view @click="changePoster" class="text">换封面</cover-view>
</cover-view>
<cover-view class="friendsCircle-top-information" @click="userClick" v-if="userInfo">
<cover-view class="friendsCircle-top-information-user">
<cover-view class="friendsCircle-top-information-nikeName">{{userInfo.nickName}}</cover-view>
<cover-image class="friendsCircle-top-information-avatar" :src="userInfo.portrait" mode="aspectFill"></cover-image>
</cover-view>
<cover-view class="friendsCircle-top-information-signature">{{userInfo.intro}}</cover-view>
</cover-view>
</video>
</view>
</template>
<script>
export default {
emits:['userClick'],
data() {
return {
CircleTop: false,
vkey: 0,
ikey:0,
videodemo: null
};
},
props:{
cover:{
type:Object,
default(){
return{
type:'img',
name:'',
url:''
}
}
},
userInfo:{
type:[Object,String]
},
showChangePoster:{
type:[Boolean],
default:true
},
},
watch:{
cover(v){
this.ikey++
this.vkey++;
}
},
methods: {
userClick(){//前往个人朋友圈
this.$emit('userClick',this.cover,this.userInfo)
},
changeCircleTop() {//顶部预览
this.CircleTop = !this.CircleTop;
if(this.type=='img'){
return
}
this.videodemo = uni.createVideoContext('videodemo', this);
this.$nextTick(() => {
setTimeout(() => {
this.vkey++;
this.$nextTick(() => {
if (this.CircleTop) {
this.videodemo.seek(0);
this.videodemo.play();
} else {
this.videodemo.pause();
this.videodemo.seek(0);
}
});
}, 105);
});
},
updateCover(formdata){
this.$http.request({
url: '/topic/editCover',
method: 'POST',
data:JSON.stringify({
cover:JSON.stringify(formdata)
}),
success: (res) => {
if (res.data.code == 200) {
this.$store.dispatch('get_UserInfo')
this.changeCircleTop()
}
}
});
},
changePoster() {//修改封面
uni.showActionSheet({
// itemList: ['图片封面', '视频封面'],
itemList: ['图片封面'],
success: (res) => {
switch (res.tapIndex){
case 0:
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album','camera'], //从相册选择
success: (res) => {
uni.showLoading({
title:'上传中'
})
this.$http.uploadFile({
url: '/file/upload',
filePath: res.tempFilePaths[0],
name: 'file',
fileType: 'image',
success: (res) => {
var data=JSON.parse(res.data)
if (data.code == 200) {
var formdata={
type:'img',
name:data.data.fileName,
url:data.data.fullPath
}
this.updateCover(formdata)
}
}
})
}
});
break;
case 1:
uni.chooseVideo({
sourceType: ['camera', 'album'],
success: (res) => {
uni.showLoading({
title:'上传中'
})
this.$http.uploadFile({
url: '/file/uploadVideo',
filePath: res.tempFilePath,
name: 'file',
fileType: 'video',
success: (res) => {
var data=JSON.parse(res.data)
if (data.code == 200) {
var formdata={
type:'video',
name:data.data.fileName,
url:data.data.fullPath,
screenShot:data.data.screenShot
}
this.updateCover(formdata)
}
}
})
}
});
break;
default:
break;
}
},
fail: function (res) {
console.log(res.errMsg);
}
});
},
},
};
</script>
<style scoped lang="scss">
.friendsCircle-top{
transition: all 0.1s;
width: 750rpx;
height: 590rpx;
position: relative;
background-image: linear-gradient( 135deg, #9708CC 10%, #43CBFF 100%);
}
.friendsCircle-top-type-img{
transition: all 0.3s;
}
.friendsCircle-top-img{
width: 100%;
height: 100%;
}
.friendsCircle-top-opend{
height: 80vh;
}
.friendsCircle-top-post-img{
width: 40rpx;
height: 40rpx;
margin: 0 auto;
}
.friendsCircle-top-post{
text-align: center;
z-index:99;
position: absolute;
bottom: 24rpx;
right: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
display: none;
}
.friendsCircle-top-post .tupian{
color: #f5f5f5;
font-size: 38rpx;
}
.friendsCircle-top-post .text{
font-size: 18rpx;
color: #f5f5f5;
}
.friendsCircle-top-video-model-img-box{
position: absolute;
width: 100%;
height: 80vh;
bottom: 0;
left: 0;
z-index: 2;
}
.friendsCircle-top-video-model-img{
width: 100%;
height: 100%;
}
.friendsCircle-top-video-model{
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 2;
// background-color: rgba(255,0,0,0.3);
}
.friendsCircle-top-opend .friendsCircle-top-post{
display: block;
}
.friendsCircle-top-video{
width: 100%;
height: 100%;
}
.friendsCircle-top-opend .friendsCircle-top-video-model-img-box{
display: none;
}
.friendsCircle-top-opend .friendsCircle-top-video{
height: 80vh;
}
.friendsCircle-top-information{
}
.friendsCircle-top-information-nikeName{
font-size: 32rpx;
color: #fff;
margin-top: 20rpx;
margin-right: 14rpx;
font-weight: bold;
text-shadow: 0px 0px 5px rgba(0,0,0,0.9);
}
.friendsCircle-top-information-avatar{
width: 120rpx;
min-width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
}
.friendsCircle-top-information{
z-index: 99;
display: flex;flex-direction: row;flex-wrap: wrap;
position: absolute;
bottom: -45rpx;
right: 24rpx;
display: flex;flex-direction: column;
}
.friendsCircle-top-information-signature{
position: absolute;
right: 0;
bottom: -45rpx;
width: 100%;
font-size: 24rpx;
color: #666;
text-align: right;
}
.friendsCircle-top-information-user{
display: flex;
flex-direction: row;
// border-radius: 12rpx;
// background-color: #f00;
}
.friendsCircle-top-opend .friendsCircle-top-information{
display: none;
}
.friendsCircle-top-video .friendsCircle-top-information{
bottom: -65rpx;
}
.friendsCircle-top-video .friendsCircle-top-information-user{
display: flex;
flex-direction: row;
}
.friendsCircle-top-video .friendsCircle-top-information-avatar{
float: left;
}
.friendsCircle-top-video .friendsCircle-top-information-nikeName{
float: left;
}
.friendsCircle-top-video .friendsCircle-top-information-signature{
position: inherit;
float: left;
margin-top: 10rpx;
}
</style>

View File

@@ -0,0 +1,145 @@
<template>
<view class="xw-tool-list">
<view class="xw-tool-list-content" v-if="type=='list'">
<template v-for="(item, i) in list" :key="i">
<view class="xw-tool-item" @click="onClick(item,i)" @longpress="onlongpress(item,i)" v-if="item.title">
<image v-if="item.icon" class="xw-tool-img" :src="item.icon" mode="aspectFill"></image>
<view class="xw-tool-text">
<uni-badge v-if="item.type == 'dottext'" text="1" is-dot absolute="rightTop" size="normal">
<text>{{ item.title }}</text>
</uni-badge>
<text v-else>{{ item.title }}</text>
</view>
<view class="xw-tool-else">
<view v-for="(v, index) in item.else" :key="index">
<uni-badge v-if="v.type == 'dotimg'" class="xw-tool-badge" text="1" is-dot absolute="rightTop" size="normal"><image :src="v.content" mode="aspectFill"></image></uni-badge>
<image v-if="v.type == 'img'" :src="v.content" mode="aspectFill"></image>
<uni-badge v-if="v.type == 'dottext'" class="xw-tool-badge" text="1" is-dot absolute="rightTop" size="normal"><view class="text">{{ v.content }}</view></uni-badge>
<view class="text" v-if="v.type == 'text'">{{ v.content }}</view>
</view>
</view>
<uni-icons v-if="!item.hideRight" class="xw-tool-right" type="right" size="16" color="#b5b5b5"></uni-icons>
</view>
</template>
</view>
<view class="xw-tool-list-content" v-if="type=='btns'">
<view class="xw-tool-btn-item" v-for="(item, i) in list" :key="i" @click="onClick(item,i)" @longpress="onlongpress(item,i)">
<view class="xw-tool-btn-icon wxfont " :class="item.icon"></view>
<view class="xw-tool-btn-text">{{item.title}}</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'toolListWx', //微信功能列表
data() {
return {};
},
emits: ['itemClick','onlongpress'],
props: {
list: {
type: Array
},
type: {
type: String,
default: 'list' //list列表icon为图片btns按钮组icon为字体
}
},
methods: {
onlongpress(e,i){
this.$emit('onlongpress', e,i);
},
onClick(e,i) {
this.$emit('itemClick', e,i);
if (e.path=='#') {
return;
}
if (!e.path&&!e.hideRight) {
uni.showToast({
title: '未开通',
icon: 'none'
});
return;
}
uni.navigateTo({
url: e.path
});
}
}
};
</script>
<style scoped>
.xw-tool-list {
display: flex;
flex-direction: column;
background-color: #ffffff;
margin-bottom: 18rpx;
}
.xw-tool-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 26rpx 24rpx;
border-bottom: 1px #eee solid;
}
.xw-tool-item:nth-last-child(1) {
border: none;
}
.xw-tool-img {
width: 52rpx;
height: 52rpx;
margin-right: 24rpx;
}
.xw-tool-text {
white-space: nowrap;
margin-right: auto;
}
.xw-tool-badge {
}
.xw-tool-else {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
}
.xw-tool-else image {
width: 60rpx;
height: 60rpx;
border-radius: 6rpx;
}
.xw-tool-else .text {
color: #666;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
max-width: 520rpx;
}
.xw-tool-else image,
.xw-tool-else .text {
margin-left: 12rpx;
}
.xw-tool-right {
margin-left: 12rpx;
}
.xw-tool-btn-item{
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 34rpx 44rpx;
border-bottom: 1px #eee solid;
font-weight: bold;
color: #5F698C;
}
.xw-tool-btn-icon{
margin-right: 14rpx;
}
</style>

View File

@@ -0,0 +1,156 @@
<template>
<view @touchmove.stop.prevent="moveHandle('touchmove')" @click="moveHandle('click')" v-if="show">
<view class="top-right-tool-wx" :animation="animationData" :style="{ top: height + 'px' }">
<view class="top-right-tool-wx-icon"></view>
<view class="top-right-tool-wx-list">
<view class="top-right-tool-wx-list-item" @click="groupChat">
<view class="top-right-tool-wx-list-item-icon"><view class="wxfont xiaoxi"></view></view>
<view class="text">发起群聊</view>
</view>
<view class="top-right-tool-wx-list-item" @click="goSearchFriends">
<view class="top-right-tool-wx-list-item-icon"><view class="wxfont jiahaoyou"></view></view>
<view class="text">添加朋友</view>
</view>
<view class="top-right-tool-wx-list-item" @click="saoyisao">
<view class="top-right-tool-wx-list-item-icon"><view class="wxfont saoyisao"></view></view>
<view class="text">扫一扫</view>
</view>
</view>
</view>
<view class="top-right-tool-wx-model"></view>
</view>
</template>
<script>
export default {
data() {
return {
height: 0 ,//距离顶部高度
show:false,
animationData: {}
};
},
props: {
list: {
type: Array,
default() {
return [{}];
}
}
},
onShow() {
},
mounted() {
this.getstatusBarHeight();
var animation = uni.createAnimation({
duration: 300,
timingFunction: 'linear'
});
this.animation = animation;
},
methods: {
groupChat(){
uni.navigateTo({
url:'../../wx/group/createGroup'
})
},
goSearchFriends(){
uni.navigateTo({
url:'../../wx/search-friends/index'
})
},
saoyisao(){
this.$fc.saoyisao()
},
showAnimation() {
this.animation.opacity(1).step();
this.animationData = this.animation.export();
},
hideAnimation() {
this.animation.opacity(0).step();
this.animationData = this.animation.export();
},
moveHandle(e) {
this.hiddenTab()
},
showTab(){
this.show=true
setTimeout(()=>{
this.showAnimation()
},30)
},
hiddenTab(){
this.show=false
this.hideAnimation()
},
getstatusBarHeight() {
var SystemInfo = uni.getSystemInfoSync();
// #ifdef H5
this.height = SystemInfo.safeArea.top + SystemInfo.windowTop;
// #endif
}
}
};
</script>
<style scoped>
.top-right-tool-wx {
width: 300rpx;
position: fixed;
z-index: 9999;
top: -10px;
right: 16rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
opacity: 0;
}
.top-right-tool-wx-model{
background-color: rgba(0,0,0,0);
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 9998;
}
.top-right-tool-wx-icon {
width: 0px;
height: 0px;
border: 10px solid transparent;
border-bottom-color: #4C4C4C;
margin-left: auto;
margin-right: 20rpx;
}
.top-right-tool-wx-list {
width: 100%;
background-color: #4C4C4C;
border-radius: 10rpx;
}
.top-right-tool-wx-list-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 0rpx 36rpx;
padding-right: 0;
}
.top-right-tool-wx-list-item-icon {
width: 38rpx;
height: 38rpx;
margin-right: 28rpx;
color: #fff;
}
.top-right-tool-wx-list-item-icon .wxfont {
font-size: 40rpx;
}
.top-right-tool-wx-list-item .text {
color: #fff;
font-size: 32rpx;
border-bottom: 1px #535353 solid;
padding: 30rpx 0rpx;
flex: 1;
}
.top-right-tool-wx-list-item:nth-last-child(1) .text{
border: none;
}
</style>

View File

@@ -0,0 +1,105 @@
<template>
<view>
<view v-if="loaded || list.itemIndex < 15" class="xw-book-wrapper">
<view v-if="list.items && list.items.length > 0" class="xw-book-sort">{{ list.key }}</view>
</view>
<view v-if="(loaded || list.itemIndex < 15) && list.items && list.items.length > 0" class="xw-book-userlist">
<view v-for="(item, index) in list.items" :key="index">
<view class="xw-book-user" @click="onClick(idx, index)">
<view v-if="showSelect" class="xw-book-user-checked">
<uni-icons :type="item.checked ? 'checkbox-filled' : 'circle'" :color="item.checked ? '#09C160' : '#C0C0C0'" size="28" />
</view>
<image class="xw-book-user-avatar" :src="item.data.avatar" mode="aspectFill"></image>
<view class="xw-book-user-name">{{item.data.name}}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'UniIndexedListWX',
emits: ['itemClick'],
props: {
loaded: {
type: Boolean,
default: false
},
idx: {
type: Number,
default: 0
},
list: {
type: Object,
default () {
return {}
}
},
showSelect: {
type: Boolean,
default: false
}
},
methods: {
onClick(idx, index) {
this.$emit("itemClick", {
idx,
index
})
}
}
}
</script>
<style lang="scss" scoped>
.xw-book-wrapper {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
}
.xw-book-sort {
padding: 0 24rpx;
flex: 1;
line-height: 60rpx;
background-color: #fafafa;
font-size: 26rpx;
}
.xw-book-userlist {
padding: 0 24rpx;
padding-right: 0;
border-radius: 24rpx;
box-shadow: 0px 0px 10rpx rgba(0, 0, 0, 0.05);
}
.xw-book-user {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
}
.xw-book-user-avatar {
width: 90rpx;
height: 90rpx;
border-radius: 10rpx;
}
.xw-book-user-name {
font-size: 32rpx;
margin-left: 26rpx;
height: 90rpx;
line-height: 90rpx;
padding-bottom: 13rpx;
padding-top: 13rpx;
border-bottom: 1px #f8f8f8 solid;
max-width: 560rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.xw-book-user-checked{
margin-right: 20rpx;
}
</style>

View File

@@ -0,0 +1,456 @@
<template>
<view class="uni-indexed-list" ref="list" id="list">
<!-- #ifdef APP-NVUE -->
<list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false">
<cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx">
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y>
<view class="xw-book-add" v-if="showAdd">
<view class="xw-book-add-item" v-for="(v,i) in addlist" :key="i" @click="gopath(v)">
<uni-badge v-if="v.num" :text="v.num+''" absolute="rightTop">
<view class="xw-book-add-icon">
<view class="wxfont jiahaoyou"></view>
</view>
</uni-badge>
<view v-else class="xw-book-add-icon" :style="{background:v.color}">
<view class="wxfont jiahaoyou"></view>
</view>
<view class="xw-book-add-item-text">{{v.title}}</view>
</view>
</view>
<view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx">
<!-- #endif -->
<indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect"
@itemClick="onClick"></indexed-list-item>
<!-- #ifndef APP-NVUE -->
</view>
</scroll-view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
</cell>
</list>
<!-- #endif -->
<view class="uni-indexed-list__menu" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
@touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove"
@mouseleave.stop="mouseleave">
<view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu--active' : ''">
<text class="uni-indexed-list__menu-text"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text>
</view>
</view>
<view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
<text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text>
</view>
</view>
</template>
<script>
import indexedListItem from './uni-indexed-list-item.vue'
// #ifdef APP-NVUE
const dom = weex.requireModule('dom');
// #endif
// #ifdef APP-PLUS
function throttle(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function touchMove(e) {
let pageY = e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
// #ifndef APP-NVUE
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #endif
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
this.touchmoveIndex = index
// #endif
}
}
const throttleTouchMove = throttle(touchMove, 40)
// #endif
/**
* IndexedList 索引列表 微信版
* @description 用于展示索引列表
* @tutorial https://ext.dcloud.net.cn/plugin?id=375
* @property {Boolean} showSelect = [true|false] 展示模式
* @value true 展示模式
* @value false 选择模式
* @property {Object} options 索引列表需要的数据对象
* @event {Function} click 点击列表事件 ,返回当前选择项的事件对象
* @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
*/
export default {
name: 'UniIndexedListWX',
components: {
indexedListItem
},
emits: ['click'],
props: {
options: {
type: Array,
default () {
return []
}
},
showSelect: {
type: Boolean,
default: false
},
showAdd: {
type: Boolean,
default: false
}
},
data() {
return {
lists: [],
winHeight: 0,
itemHeight: 0,
winOffsetY: 0,
touchmove: false,
touchmoveIndex: -1,
scrollViewId: '',
touchmovable: true,
loaded: false,
isPC: false
}
},
computed:{
friendApply() {
return this.$store.state.friendApply
},
addlist(){
return [{
title:'新的朋友',
path:'../../wx/search-friends/index',
num:this.$store.state.friendApply.count ? this.$store.state.friendApply.count : '',
icon:'jiahaoyou',
color:'#FB9E3E'
},{
title:'群聊',
path:'../../wx/groupInfo/grouplist',
icon:'qunl',
num:'',
color:'#0ABF62'
}]
}
},
watch: {
options: {
handler: function() {
this.setList()
},
deep: true
}
},
mounted() {
// #ifdef H5
this.isPC = this.IsPC()
// #endif
setTimeout(() => {
this.setList()
}, 50)
setTimeout(() => {
this.loaded = true
}, 300);
},
methods: {
gopath(e){
uni.navigateTo({
url:e.path
})
},
setList() {
let index = 0;
this.lists = []
this.options.forEach((value, index) => {
if (value.data.length === 0) {
return
}
let indexBefore = index
let items = value.data.map(item => {
let obj = {}
obj['key'] = value.letter
obj['data'] = item
obj['itemIndex'] = index
index++
obj.checked = item.checked ? item.checked : false
return obj
})
this.lists.push({
title: value.letter,
key: value.letter,
items: items,
itemIndex: indexBefore
})
})
// #ifndef APP-NVUE
uni.createSelectorQuery()
.in(this)
.select('#list')
.boundingClientRect()
.exec(ret => {
this.winOffsetY = ret[0].top
this.winHeight = ret[0].height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['list'], (res) => {
this.winOffsetY = res.size.top
this.winHeight = res.size.height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
},
touchStart(e) {
this.touchmove = true
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
// #endif
}
},
touchMove(e) {
// #ifndef APP-PLUS
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
}
// #endif
// #ifdef APP-PLUS
throttleTouchMove.call(this, e)
// #endif
},
touchEnd() {
this.touchmove = false
// this.touchmoveIndex = -1
},
/**
* 兼容 PC @tian
*/
mousedown(e) {
if (!this.isPC) return
this.touchStart(e)
},
mousemove(e) {
if (!this.isPC) return
this.touchMove(e)
},
mouseleave(e) {
if (!this.isPC) return
this.touchEnd(e)
},
// #ifdef H5
IsPC() {
var userAgentInfo = navigator.userAgent;
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (let v = 0; v < Agents.length - 1; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
},
// #endif
onClick(e) {
let {
idx,
index
} = e
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
let select = []
if (this.showSelect) {
this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
this.lists.forEach((value, idx) => {
value.items.forEach((item, index) => {
if (item.checked) {
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
select.push(obj)
}
})
})
}
this.$emit('click', {
item: obj,
select: select
})
}
}
}
</script>
<style lang="scss" scoped>
.uni-indexed-list {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
}
.uni-indexed-list__scroll {
flex: 1;
height: 100%;
}
.uni-indexed-list__menu {
width: 24px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-indexed-list__menu-item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
align-items: center;
justify-content: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-indexed-list__menu-text {
font-size: 12px;
text-align: center;
color: #aaa;
}
.uni-indexed-list__menu--active {
// background-color: rgb(200, 200, 200);
}
.uni-indexed-list__menu--active {}
.uni-indexed-list__menu-text--active {
border-radius: 16px;
width: 16px;
height: 16px;
line-height: 16px;
// background-color: #007aff;
// color: #fff;
}
.uni-indexed-list__alert-wrapper {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.uni-indexed-list__alert {
width: 80px;
height: 80px;
border-radius: 80px;
text-align: center;
line-height: 80px;
font-size: 35px;
color: #fff;
background-color: rgba(0, 0, 0, 0.5);
}
.h5-buchang{
height: 140rpx;
display: none;
}
.uni-scroll-view-content view:nth-last-child(1) .h5-buchang{
display: block;
}
.xw-book-add{
width: 100%;
display: flex;
flex-direction: column;
padding:0 24rpx;
box-sizing: border-box;
// height: 120rpx;
}
.xw-book-add-item{
display: flex;flex-direction: row;align-items: center;
// height: 90rpx;
// line-height: 90rpx;
padding-bottom: 16rpx;
padding-top: 16rpx;
// border-bottom: 1px #f8f8f8 solid;
}
.xw-book-add-icon{
background: #FB9E3E;
border-radius: 10rpx;
width: 90rpx;
height: 90rpx;
display: flex;flex-direction: row;align-items: center;justify-content: center;
}
.xw-book-add-icon .wxfont{
color: #fff;
font-size: 58rpx;
}
.xw-book-add-item-text{
flex: 1;
font-size: 32rpx;
margin-left: 26rpx;
// height: 90rpx;
// line-height: 90rpx;
// border-bottom: 1px #f8f8f8 solid;
}
</style>

View File

@@ -0,0 +1,173 @@
<template>
<view @touchmove.stop.prevent="moveHandle('touchmove')" @click="moveHandle('click')" v-if="show">
<view class="openTool-wx" :animation="animationData">
<view class="openTool-wx-list" v-if="data">
<view class="openTool-wx-list-item" @click="zhiding">
<view class="text" v-if="data.top=='Y'">取消置顶</view>
<view class="text" v-if="data.top=='N'">置顶该聊天</view>
</view>
<view class="openTool-wx-list-item" @click="shanchu">
<view class="text">删除该聊天</view>
</view>
</view>
</view>
<view class="openTool-wx-model"></view>
</view>
</template>
<script>
export default {
data() {
return {
show:false,
animationData: {}
};
},
props: {
data:{
type:[Object,String]
},
itemKey:{
type:[Object,String,Number]
},
list: {
type: Array,
default() {
return [{}];
}
}
},
onShow() {
},
computed: {
chatList() {
return this.$store.state.chatlist
}
},
mounted() {
var animation = uni.createAnimation({
duration: 300,
timingFunction: 'linear'
});
this.animation = animation;
},
methods: {
zhiding(){
var data=JSON.parse(JSON.stringify(this.data))
var yn = data.top=='N' ? 'Y' : 'N';
data.top=yn
this.$store.dispatch('updateChatListInfoById', {
userId: this.data.userId,
data: data
})
this.$store.dispatch('getChatList')
if(data.windowType=='GROUP'){
var formData = {
groupId: this.data.userId,
top: yn
};
this.$http.request({
url: '/group/editTop',
method: 'POST',
data: JSON.stringify(formData),
success: res => {
if (res.data.code == 200) {
}
}
});
}
if(data.windowType=='SINGLE'){
var formData = {
userId: this.data.userId,
top: yn
};
this.$http.request({
url: '/friend/top',
method: 'POST',
data: JSON.stringify(formData),
success: res => {
if (res.data.code == 200) {
}
}
});
}
},
shanchu(){
delete this.chatList[this.data.userId]
this.$store.dispatch('updateChatListInfoById',{
userId: this.data.userId,
data: {}
});
this.$store.dispatch('updateChatById', {
userId: this.data.userId,
data: []
});
},
showAnimation() {
this.animation.opacity(1).step();
this.animationData = this.animation.export();
},
hideAnimation() {
this.animation.opacity(0).step();
this.animationData = this.animation.export();
},
moveHandle(e) {
this.hiddenTab()
},
showTab(){
this.show=true
setTimeout(()=>{
this.showAnimation()
},30)
},
hiddenTab(){
this.show=false
this.hideAnimation()
},
}
};
</script>
<style scoped>
.openTool-wx {
display: flex;
flex-direction: column;
flex-wrap: wrap;
opacity: 0;
position: relative;
z-index: 2;
}
.openTool-wx-model{
background-color: rgba(0,0,0,0);
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 1;
}
.openTool-wx-list {
width: 100%;
box-sizing: border-box;
background-color: #fff;
box-shadow: 0px 0px 10px rgba(0,0,0,0.3);
border-radius: 10rpx;
display: flex;
flex-direction: column;
}
.openTool-wx-list-item {
padding: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.openTool-wx-list-item .text {
color: #333;
font-size: 28rpx;
flex: 1;
}
.openTool-wx-list-item:nth-last-child(1) .text{
border: none;
}
</style>

View File

@@ -0,0 +1,583 @@
<template>
<!-- #ifdef APP-NVUE -->
<cell>
<!-- #endif -->
<view :hover-class="!clickable && !link ? '' : 'uni-list-chat--hover'" :style="{background:color}" class="uni-list-chat" @longpress="longpressItem($event,itemKey,item)">
<openTool class="openTool" :ref="'toolx'+itemKey" :data="item" :itemKey="itemKey"></openTool>
<view :class="{ 'uni-list--border': border, 'uni-list-chat--first': isFirstChild }"></view>
<view class="uni-list-chat__container" @click="onClick">
<view class="uni-list-chat__header-warp">
<view v-if="item.windowType=='SINGLE'" class="uni-list-chat__header" :class="{ 'header--circle': avatarCircle }">
<image class="uni-list-chat__header-image" :src="item.portrait" mode="aspectFill"></image>
</view>
<!-- 头像组 -->
<view v-if="item.windowType=='GROUP'" class="uni-list-chat__header">
<view v-for="(item, index) in avatarList" :key="index" class="uni-list-chat__header-box" :class="computedAvatar"
:style="{ width: imageWidth + 'px', height: imageWidth + 'px' }">
<image class="uni-list-chat__header-image" :style="{ width: imageWidth + 'px', height: imageWidth + 'px' }" :src="item.url"
mode="aspectFill"></image>
</view>
</view>
</view>
<view v-if="badgeText && badgePositon === 'left'" class="uni-list-chat__badge uni-list-chat__badge-pos" :class="[isSingle]">
<text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text>
</view>
<view class="uni-list-chat__content">
<view class="uni-list-chat__content-main">
<text class="uni-list-chat__content-title uni-ellipsis">{{ title }}</text>
<text class="uni-list-chat__content-note uni-ellipsis">{{ note }}</text>
</view>
<view class="uni-list-chat__content-extra">
<slot>
<text class="uni-list-chat__content-extra-text">{{ time }}</text>
<view v-if="badgeText && badgePositon === 'right'" class="uni-list-chat__badge" :class="[isSingle, badgePositon === 'right' ? 'uni-list-chat--right' : '']">
<text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text>
</view>
</slot>
</view>
</view>
</view>
</view>
<!-- #ifdef APP-NVUE -->
</cell>
<!-- #endif -->
</template>
<script>
import openTool from './openTool.vue'
// 头像大小
const avatarWidth = 45;
/**
* ListChat 聊天列表
* @description 聊天列表,用于创建聊天类列表
* @tutorial https://ext.dcloud.net.cn/plugin?id=24
* @property {String} title 标题
* @property {String} note 描述
* @property {Boolean} clickable = [true|false] 是否开启点击反馈默认为false
* @property {String} badgeText 数字角标内容
* @property {String} badgePositon = [left|right] 角标位置,默认为 right
* @property {String} link = [falsenavigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈默认为false
* @value false 不开启
* @value navigateTo 同 uni.navigateTo()
* @value redirectTo 同 uni.redirectTo()
* @value reLaunch 同 uni.reLaunch()
* @value switchTab 同 uni.switchTab()
* @property {String | PageURIString} to 跳转目标页面
* @property {String} time 右侧时间显示
* @property {Boolean} avatarCircle = [true|false] 是否显示圆形头像默认为false
* @property {String} avatar 头像地址avatarCircle 不填时生效
* @property {Array} avatarList 头像组,格式为 [{url:''}]
* @event {Function} click 点击 uniListChat 触发事件
*/
export default {
components:{
openTool
},
name: 'UniListChatWx',
emits:['click','longpressItem'],
props: {
color: {
type: String,
default: '#fff'
},
title: {
type: String,
default: ''
},
note: {
type: String,
default: ''
},
clickable: {
type: Boolean,
default: false
},
link: {
type: [Boolean, String],
default: false
},
to: {
type: String,
default: ''
},
badgeText: {
type: [String, Number],
default: ''
},
badgePositon: {
type: String,
default: 'right'
},
time: {
type: String,
default: ''
},
avatarCircle: {
type: Boolean,
default: false
},
avatar: {
type: String,
default: ''
},
// avatarList: {
// type: Array,
// default () {
// return [];
// }
// },
item: {
type: Object,
default: {}
},
itemKey: {
type: Number
},
longTapItemKey: {
type: [Number,String],
default:''
}
},
// inject: ['list'],
computed: {
isSingle() {
if (this.badgeText === 'dot') {
return 'uni-badge--dot';
} else {
const badgeText = this.badgeText.toString();
if (badgeText.length > 1) {
return 'uni-badge--complex';
} else {
return 'uni-badge--single';
}
}
},
avatarList(){
return this.returnAvatar(this.item.portrait)
},
computedAvatar() {
if (this.avatarList.length > 4) {
this.imageWidth = avatarWidth * 0.31;
return 'avatarItem--3';
} else if (this.avatarList.length > 1) {
this.imageWidth = avatarWidth * 0.47;
return 'avatarItem--2';
} else {
this.imageWidth = avatarWidth;
return 'avatarItem--1';
}
}
},
data() {
return {
isFirstChild: false,
border: true,
// avatarList: 3,
imageWidth: 50
};
},
mounted() {
this.list = this.getForm()
if (this.list) {
if (!this.list.firstChildAppend) {
this.list.firstChildAppend = true;
this.isFirstChild = true;
}
this.border = this.list.border;
}
},
methods: {
returnAvatar(text){
var data=JSON.parse(text)
var avatars=[]
for(var i=0;i<data.length;i++){
avatars.push({
url:data[i]
})
}
return avatars
},
longpressItem(e,i,v) {//长按回调
this.$emit('longpressItem',e,i,v)
if(this.itemKey==this.longTapItemKey){
this.$refs['toolx'+this.itemKey].showTab();
}
},
/**
* 获取父元素实例
*/
getForm(name = 'uniList') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
onClick() {
if (this.to !== '') {
this.openPage();
return;
}
if (this.clickable || this.link) {
this.$emit('click', {
data: {}
});
}
},
openPage() {
if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].indexOf(this.link) !== -1) {
this.pageApi(this.link);
} else {
this.pageApi('navigateTo');
}
},
pageApi(api) {
uni[api]({
url: this.to,
success: res => {
this.$emit('click', {
data: res
});
},
fail: err => {
this.$emit('click', {
data: err
});
console.error(err.errMsg);
}
});
}
}
};
</script>
<style lang="scss" scoped>
$uni-font-size-lg:16px;
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
$background-color: #fff;
$divide-line-color: #e5e5e5;
$avatar-width: 45px;
$avatar-border-radius: 5px;
$avatar-border-color: #eee;
$avatar-border-width: 1px;
$title-size: 16px;
$title-color: #3b4144;
$title-weight: normal;
$note-size: 12px;
$note-color: #999;
$note-weight: normal;
$right-text-size: 12px;
$right-text-color: #999;
$right-text-weight: normal;
$badge-left: 0px;
$badge-top: 0px;
$dot-width: 10px;
$dot-height: 10px;
$badge-size: 18px;
$badge-font: 12px;
$badge-color: #fff;
$badge-background-color: #ff5a5f;
$badge-space: 6px;
$hover: #f5f5f5;
.openTool{
width: 100%;
position: absolute;
top: 50%;
display: flex;flex-direction: row;justify-content: center;
}
.uni-list-chat {
font-size: $uni-font-size-lg;
position: relative;
flex-direction: column;
justify-content: space-between;
background-color: $background-color;
position: relative;
}
// .uni-list-chat--disabled {
// opacity: 0.3;
// }
.uni-list-chat--hover {
background-color: $hover;
}
.uni-list--border {
position: relative;
margin-left: $uni-spacing-row-lg;
/* #ifdef APP-PLUS */
border-top-color: $divide-line-color;
border-top-style: solid;
border-top-width: 0.5px;
/* #endif */
}
/* #ifndef APP-NVUE */
.uni-list--border:after {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $divide-line-color;
}
.uni-list-item--first:after {
height: 0px;
}
/* #endif */
.uni-list-chat--first {
border-top-width: 0px;
}
.uni-ellipsis {
/* #ifndef APP-NVUE */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
}
.uni-ellipsis-2 {
/* #ifndef APP-NVUE */
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
/* #endif */
/* #ifdef APP-NVUE */
lines: 2;
/* #endif */
}
.uni-list-chat__container {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
padding: $uni-spacing-row-base $uni-spacing-row-lg;
position: relative;
overflow: hidden;
}
.uni-list-chat__header-warp {
position: relative;
}
.uni-list-chat__header {
/* #ifndef APP-NVUE */
display: flex;
align-content: center;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: wrap-reverse;
/* #ifdef APP-NVUE */
width: 50px;
height: 50px;
/* #endif */
/* #ifndef APP-NVUE */
width: $avatar-width;
height: $avatar-width;
/* #endif */
border-radius: $avatar-border-radius;
border-color: $avatar-border-color;
border-width: $avatar-border-width;
border-style: solid;
overflow: hidden;
}
.uni-list-chat__header-box {
/* #ifndef APP-PLUS */
box-sizing: border-box;
display: flex;
width: $avatar-width;
height: $avatar-width;
/* #endif */
/* #ifdef APP-NVUE */
width: 50px;
height: 50px;
/* #endif */
overflow: hidden;
border-radius: 2px;
}
.uni-list-chat__header-image {
margin: 1px;
/* #ifdef APP-NVUE */
width: 50px;
height: 50px;
/* #endif */
/* #ifndef APP-NVUE */
width: $avatar-width;
height: $avatar-width;
/* #endif */
}
/* #ifndef APP-NVUE */
.uni-list-chat__header-image {
display: block;
width: 100%;
height: 100%;
}
.avatarItem--1 {
width: 100%;
height: 100%;
}
.avatarItem--2 {
width: 47%;
height: 47%;
}
.avatarItem--3 {
width: 32%;
height: 32%;
}
/* #endif */
.header--circle {
border-radius: 50%;
}
.uni-list-chat__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
overflow: hidden;
padding: 2px 0;
}
.uni-list-chat__content-main {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: space-between;
padding-left: $uni-spacing-row-base;
flex: 1;
overflow: hidden;
}
.uni-list-chat__content-title {
font-size: $title-size;
color: $title-color;
font-weight: $title-weight;
overflow: hidden;
}
.uni-list-chat__content-note {
margin-top: 3px;
color: $note-color;
font-size: $note-size;
font-weight: $title-weight;
overflow: hidden;
}
.uni-list-chat__content-extra {
/* #ifndef APP-NVUE */
flex-shrink: 0;
display: flex;
/* #endif */
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
margin-left: 5px;
}
.uni-list-chat__content-extra-text {
color: $right-text-color;
font-size: $right-text-size;
font-weight: $right-text-weight;
overflow: hidden;
}
.uni-list-chat__badge-pos {
position: absolute;
/* #ifdef APP-NVUE */
left: 55px;
top: 3px;
/* #endif */
/* #ifndef APP-NVUE */
left: calc(#{$avatar-width} + 10px - #{$badge-space} + #{$badge-left});
top: calc(#{$uni-spacing-row-base}/ 2 + 1px + #{$badge-top});
/* #endif */
}
.uni-list-chat__badge {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
border-radius: 100px;
background-color: $badge-background-color;
}
.uni-list-chat__badge-text {
color: $badge-color;
font-size: $badge-font;
}
.uni-badge--single {
/* #ifndef APP-NVUE */
// left: calc(#{$avatar-width} + 7px + #{$badge-left});
/* #endif */
width: $badge-size;
height: $badge-size;
}
.uni-badge--complex {
/* #ifdef APP-NVUE */
left: 50px;
/* #endif */
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
height: $badge-size;
padding: 0 $badge-space;
}
.uni-badge--dot {
/* #ifdef APP-NVUE */
left: 60px;
top: 6px;
/* #endif */
/* #ifndef APP-NVUE */
left: calc(#{$avatar-width} + 15px - #{$dot-width}/ 2 + 1px + #{$badge-left});
/* #endif */
width: $dot-width;
height: $dot-height;
padding: 0;
}
.uni-list-chat--right {
/* #ifdef APP-NVUE */
left: 0;
/* #endif */
}
</style>

View File

@@ -0,0 +1,108 @@
<template>
<!-- #ifndef APP-NVUE -->
<view class="uni-list uni-border-top-bottom">
<view v-if="border" class="uni-list--border-top"></view>
<slot />
<view v-if="border" class="uni-list--border-bottom"></view>
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<list class="uni-list" :class="{ 'uni-list--border': border }" :enableBackToTop="enableBackToTop" loadmoreoffset="15"><slot /></list>
<!-- #endif -->
</template>
<script>
/**
* List 列表
* @description 列表组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=24
* @property {String} border = [true|false] 标题
*/
export default {
name: 'uniListWx',
'mp-weixin': {
options: {
multipleSlots: false
}
},
props: {
enableBackToTop: {
type: [Boolean, String],
default: false
},
scrollY: {
type: [Boolean, String],
default: false
},
border: {
type: Boolean,
default: true
}
},
// provide() {
// return {
// list: this
// };
// },
created() {
this.firstChildAppend = false;
},
methods: {
loadMore(e) {
this.$emit('scrolltolower');
}
}
};
</script>
<style lang="scss" scoped>
$uni-bg-color:#ffffff;
$uni-border-color:#e5e5e5;
.uni-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
background-color: $uni-bg-color;
position: relative;
flex-direction: column;
}
.uni-list--border {
position: relative;
/* #ifdef APP-NVUE */
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 0.5px;
border-bottom-color: $uni-border-color;
border-bottom-style: solid;
border-bottom-width: 0.5px;
/* #endif */
z-index: -1;
}
/* #ifndef APP-NVUE */
.uni-list--border-top {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $uni-border-color;
z-index: 1;
}
.uni-list--border-bottom {
position: absolute;
bottom: 0;
right: 0;
left: 0;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $uni-border-color;
}
/* #endif */
</style>

View File

@@ -0,0 +1,140 @@
<template>
<view class="uni-section">
<view class="uni-section-header" nvue>
<view v-if="type" class="uni-section__head">
<view :class="type" class="uni-section__head-tag"/>
</view>
<view class="uni-section__content">
<text :class="{'distraction':!subTitle}" :style="{color:color}" class="uni-section__content-title">{{ title }}</text>
<text v-if="subTitle" class="uni-section__content-sub">{{ subTitle }}</text>
</view>
</view>
<view :style="{padding: padding ? '10px' : ''}">
<slot/>
</view>
</view>
</template>
<script>
/**
* Section 标题栏
* @description 标题栏
* @property {String} type = [line|circle] 标题装饰类型
* @value line 竖线
* @value circle 圆形
* @property {String} title 主标题
* @property {String} subTitle 副标题
*/
export default {
name: 'UniSection',
emits:['click'],
props: {
type: {
type: String,
default: ''
},
title: {
type: String,
default: ''
},
color:{
type: String,
default: '#333'
},
subTitle: {
type: String,
default: ''
},
padding: {
type: Boolean,
default: false
}
},
data() {
return {}
},
watch: {
title(newVal) {
if (uni.report && newVal !== '') {
uni.report('title', newVal)
}
}
},
methods: {
onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped>
$uni-primary: #2979ff !default;
.uni-section {
background-color: #fff;
// overflow: hidden;
margin-top: 10px;
}
.uni-section-header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 12px 10px;
// height: 50px;
font-weight: normal;
}
.uni-section__head {
flex-direction: row;
justify-content: center;
align-items: center;
margin-right: 10px;
}
.line {
height: 12px;
background-color: $uni-primary;
border-radius: 10px;
width: 4px;
}
.circle {
width: 8px;
height: 8px;
border-top-right-radius: 50px;
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
border-bottom-right-radius: 50px;
background-color: $uni-primary;
}
.uni-section__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
flex: 1;
color: #333;
}
.uni-section__content-title {
font-size: 14px;
color: $uni-primary;
}
.distraction {
flex-direction: row;
align-items: center;
}
.uni-section__content-sub {
font-size: 12px;
color: #999;
line-height: 16px;
margin-top: 2px;
}
</style>

View File

@@ -0,0 +1,20 @@
<template>
<zmm-watermark :opacity="0.05" :watermark="watermark"></zmm-watermark>
</template>
<script>
export default {
name: 'watermark',
data() {
return {};
},
computed: {
watermark() {
return this.$store.state.watermark;
}
}
};
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,187 @@
<template>
<view class="zmm-picker-region" :style="{'cursor':disabled ? 'move' : 'default'}">
<picker
:disabled="disabled"
mode="multiSelector"
@columnchange="Citycolumnchange($event)"
@change="pickerchange"
:value="Index"
:range="pickerArr"
range-key="label"
@tap="iconopen"
@cancel="opend = false"
>
<view class="zmm-picker-region-uni-input" :style="{'color':disabled ? '#666' : '#333'}" :class="{ 'zmm-picker-region-placeholder': modelValue == '' }">
<text :style="{ 'text-align': textAlign }" v-if="modelValue == ''">{{ placeholder }}</text>
<text :style="{ 'text-align': textAlign }" v-else>{{ modelValue }}</text>
</view>
</picker>
</view>
</template>
<script>
import citydata from '@/common/city.js';
export default {
emits: ['update:modelValue','change'],
data() {
return {
opend: false,
Index: [0,0,0],
citydata:citydata.data
};
},
behaviors: ['uni://form-field'], //必须要写不然微信小程序收不到值
props: {
rangeLeave:{//联动级别
type:Number,
default:2
},
textAlign: {
type: String,
default: 'center'
},
disabled: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: '请点击选择'
},
modelValue: {
type: [String]
}
},
mounted() {
},
computed:{
pickerArr(){
var arr=[]
function findarr(arrx){
var arr=[]
for (var i = 0; i < arrx.length; i++) {
var item = arrx[i]
arr.push({
label:item.label,
value:item.value,
position:item.position
})
}
return arr
}
for (var i = 0; i < this.rangeLeave; i++) {
arr.push([])
if(i==0){//一级数据
arr[i]=findarr(this.citydata)
}
if(i==1){//二级数据
if(this.citydata[this.Index[0]]){
arr[i]=findarr(this.citydata[this.Index[0]]['children'])
}else{
arr[i]=[]
}
}
if(i==2){//三级数据
if(this.citydata[this.Index[0]]&&this.citydata[this.Index[0]]['children'][this.Index[1]]){
arr[i]=findarr(this.citydata[this.Index[0]]['children'][this.Index[1]]['children'])
}else{
arr[i]=[]
}
}
}
return arr
}
},
watch: {
modelValue:{
deep: true,
immediate:true,
handler(val) {
this.iniIndex()
}
}
},
methods: {
iniIndex(){
var arr=this.modelValue.split(',')
function findLabelIndex(arr,label){
if(!label){
return 0
}
for (var i = 0; i < arr.length; i++) {
if(arr[i].label==label){
return i
break;
}
}
return 0
}
for (var i = 0; i < arr.length; i++) {
this.Index[i]=findLabelIndex(this.pickerArr[i],arr[i])
}
},
iconopen() {
if (this.disabled) {
return;
}
this.opend = true;
},
pickerchange(e) {
var text=[]
if(this.rangeLeave==1){
text.push(this.pickerArr[0][e.detail.value[0]].label)
}
if(this.rangeLeave==2){
text.push(this.pickerArr[0][e.detail.value[0]].label)
text.push(this.pickerArr[1][e.detail.value[1]].label)
}
if(this.rangeLeave==3){
text.push(this.pickerArr[0][e.detail.value[0]].label)
text.push(this.pickerArr[1][e.detail.value[1]].label)
text.push(this.pickerArr[2][e.detail.value[2]].label)
}
this.opend = !this.opend;
this.$emit('update:modelValue', text.toString());
this.$emit('change', text.toString())
},
Citycolumnchange(e) {
if (e.detail.column == 0&&this.rangeLeave>1) {//第一行发生变化重置第二列索引
this.Index.splice(1, 1, 0);
}
if (e.detail.column == 1&&this.rangeLeave>2) {
this.Index.splice(2, 1, 0);
}
this.Index[e.detail.column] = e.detail.value;//设置index
this.$forceUpdate();
},
}
};
</script>
<style>
.zmm-picker-region {
}
.zmm-picker-region-uni-input {
height: 90rpx;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: #fff;
}
.zmm-picker-region-uni-input text {
flex: 1;
font-size: 28rpx;
padding:0 12rpx;
}
.zmm-picker-region-placeholder {
width: 100%;
}
.zmm-picker-region-placeholder text{
color: #666;
}
</style>

View File

@@ -0,0 +1,105 @@
<template>
<view>
<radio-group class="zmm-radio-group" @change="radioChange">
<label class="zmm-radio-group-label" v-for="(item,index) in ranges" :key="index" :class="{'checked':index === current}">
<view class="zmm-radio-group-radio">
<radio style="transform:scale(0.9)" :value="item.value" :checked="index === current" />
</view>
<view class="zmm-radio-group-label-text">{{ item.label }}</view>
<uni-icons class="zmm-radio-group-label-icon" type="checkmarkempty" color="#09C160" size="20"></uni-icons>
</label>
</radio-group>
</view>
</template>
<script>
export default {
emits: ['update:modelValue','change'],
data() {
return {
current: '',
val: '',
ranges: []
};
},
props: {
modelValue: {
type: [String]
},
range: {
//数组label value
type: Array,
default: []
}
},
watch: {
range(v) {
this.init();
},
modelValue(val) {
this.init();
},
},
created() {
this.init();
},
methods: {
init() {
this.ranges = JSON.parse(JSON.stringify(this.range));
this.getval(this.modelValue);
},
getval(e) {
if (!e) {
return;
}
var val = e;
var items = this.ranges;
for (var i = 0; i < items.length; ++i) {
const item = items[i];
if (item.value==val) {
this.current=i
break;
}
}
},
radioChange(e) {
this.getval(e.detail.value);
this.$emit('update:modelValue', e.detail.value)
this.$emit('change', e.detail.value)
}
}
};
</script>
<style>
.zmm-radio-group {
display: flex;
flex-direction: column;
}
.zmm-radio-group-radio{
display: none;
}
.zmm-radio-group-label.checked{
}
.zmm-radio-group-label-icon{display: none;}
.zmm-radio-group-label.checked .zmm-radio-group-label-icon{
display: block;
}
.zmm-radio-group-label {
height: 90rpx;
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
background-color: #fff;
border-bottom: 1px #eee solid;
padding: 24rpx;
}
.zmm-radio-group-label:nth-last-child(1){
margin-right: 0;
}
.zmm-radio-group-label-text{
margin-left: 10rpx;
font-size: 28rpx;
margin-right: auto;
}
</style>

View File

@@ -0,0 +1,277 @@
<template>
<view v-if="show">
<view class="uni-padding-wrap">
<block v-if="!recording && !playing && !hasRecord">
<view class="page-body-time">
<text class="time-big">{{formatedRecordTime}}</text>
</view>
<view class="page-body-buttons">
<view class="page-body-button" @click="startRecord">
<image src="@/static/record.png"></image>
</view>
</view>
</block>
<block v-if="recording === true">
<view class="page-body-time">
<text class="time-big">{{formatedRecordTime}}</text>
</view>
<view class="page-body-buttons">
<view class="page-body-button" @click="stopRecord">
<view class="button-stop-record"></view>
</view>
</view>
</block>
<block v-if="hasRecord === true && playing === false">
<view class="page-body-time">
<text class="time-big">{{formatedPlayTime}}</text>
<text class="time-small">{{formatedRecordTime}}</text>
</view>
<view class="page-body-buttons">
<view class="page-body-button" @click="playVoice">
<image src="@/static/play.png"></image>
</view>
<view class="page-body-button" @click="clear">
<image src="@/static/trash.png"></image>
</view>
</view>
</block>
<block v-if="hasRecord === true && playing === true">
<view class="page-body-time">
<text class="time-big">{{formatedPlayTime}}</text>
<text class="time-small">{{formatedRecordTime}}</text>
</view>
<view class="page-body-buttons">
<view class="page-body-button" @click="stopVoice">
<image src="@/static/stop.png"></image>
</view>
<view class="page-body-button" @click="clear">
<image src="@/static/trash.png"></image>
</view>
</view>
</block>
</view>
</view>
</template>
<script>
// #ifdef APP-PLUS
import permision from "@/common/permission.js"
// #endif
var playTimeInterval = null;
var recordTimeInterval = null;
var recorderManager = null;
var music = null;
export default {
props:{
show:{
type:Boolean,
default:true
}
},
emits:['recorderStop'],
data() {
return {
recording: false, //录音中
playing: false, //播放中
hasRecord: false, //是否有了一个
tempFilePath: '',
recordTime: 0,
playTime: 0,
formatedRecordTime: '00:00:00', //录音的总时间
formatedPlayTime: '00:00:00' //播放录音的当前时间
}
},
beforeDestroy: function() {
this.clear();
},
mounted() {
music = uni.createInnerAudioContext();
music.onEnded(() => {
clearInterval(playTimeInterval)
var playTime = 0
// console.log('play voice finished')
this.playing = false;
this.formatedPlayTime = this.formatTime(playTime);
this.playTime = playTime;
});
recorderManager = uni.getRecorderManager();
recorderManager.onStart(() => {
// console.log('recorder start');
this.recording = true;
recordTimeInterval = setInterval(() => {
this.recordTime += 1;
this.formatedRecordTime = this.formatTime(this.recordTime);
}, 1000)
});
recorderManager.onStop((res) => {
// console.log('on stop');
music.src = res.tempFilePath;
this.hasRecord = true;
this.recording = false;
this.$emit('recorderStop',{
formatedRecordTime:this.formatedRecordTime,
recordTime:this.recordTime<1 ? this.recordTime+1 : this.recordTime,
recordFilePath:res.tempFilePath
})
this.clear()
});
recorderManager.onError(() => {
console.log('recorder onError');
});
},
methods: {
formatTime(time) {
if (typeof time !== 'number' || time < 0) {
return time
}
var hour = parseInt(time / 3600)
time = time % 3600
var minute = parseInt(time / 60)
time = time % 60
var second = time
return ([hour, minute, second]).map(function(n) {
n = n.toString()
return n[1] ? n : '0' + n
}).join(':')
},
async startRecord() { //开始录音
// #ifdef APP-PLUS
let status = await this.checkPermission();
if (status !== 1) {
return;
}
// #endif
// TODO ios 在没有请求过权限之前无法得知是否有相关权限,这种状态下需要直接调用录音,但没有状态或回调判断用户拒绝
recorderManager.start({
duration:600000,
sampleRate:44100,
format:'mp3'
});
},
stopRecord() { //停止录音
recorderManager.stop();
},
playVoice() {
// console.log('play voice');
this.playing = true;
playTimeInterval = setInterval(() => {
this.playTime += 1;
this.formatedPlayTime = this.formatTime(this.playTime);
}, 1000)
music.play();
},
stopVoice() {
clearInterval(playTimeInterval)
this.playing = false;
this.formatedPlayTime = this.formatTime(0);
this.playTime = 0;
music.stop();
},
end() {
music.stop();
recorderManager.stop();
clearInterval(recordTimeInterval)
clearInterval(playTimeInterval);
this.recording = false, this.playing = false, this.hasRecord = false;
this.playTime = 0, this.recordTime = 0;
this.formatedRecordTime = "00:00:00", this.formatedRecordTime = "00:00:00";
},
clear() {
this.end();
}
// #ifdef APP-PLUS
,
async checkPermission() {
let status = permision.isIOS ? await permision.requestIOS('record') :
await permision.requestAndroid('android.permission.RECORD_AUDIO');
if (status === null || status === 1) {
status = 1;
} else if (status === 2) {
uni.showModal({
content: "系统麦克风已关闭",
confirmText: "确定",
showCancel: false,
success: function(res) {
}
})
} else {
uni.showModal({
content: "需要麦克风权限",
confirmText: "设置",
success: function(res) {
if (res.confirm) {
permision.gotoAppSetting();
}
}
})
}
return status;
}
// #endif
}
}
</script>
<style scoped>
.uni-padding-wrap{}
image {
width: 130rpx;
height: 130rpx;
}
.page-body-wrapper {
justify-content: space-between;
flex-grow: 1;
margin-bottom: 300rpx;
}
.page-body-time {
display: flex;
flex-direction: column;
align-items: center;
}
.time-big {
font-size: 26rpx;
margin: 20rpx;
}
.time-small {
font-size: 26rpx;
}
.page-body-buttons{
display: flex;
justify-content: space-around;
}
.page-body-button {
/* width: 250rpx; */
text-align: center;
}
.button-stop-record {
box-sizing: border-box;
width: 130rpx;
height: 130rpx;
border: 20rpx solid #fff;
background-color: #4CD964;
border-radius: 50%;
animation: colors 1s linear infinite;
}
@keyframes colors {
0% {
opacity: 1;
}
50% {
opacity: .7;
}
100% {
opacity: 1;
}
}
</style>

View File

@@ -0,0 +1,158 @@
<template>
<view>
<image class="zmm-upload-avatar" :src="modelValue" :style="{ width: imgSize + 'rpx', height: imgSize + 'rpx', 'border-radius': imgRadius + 'rpx' }" mode="aspectFill" @click="selectImage"></image>
</view>
</template>
<script>
import http from '@/common/request.js';
export default {
name: 'zmm-upload-avatar',
emits: ['update:modelValue','change'],
props: {
modelValue: {
type: [String]
},
imgSize: {
//图片大小
type: Number,
default: 120
},
imgRadius: {
//图片圆角
type: Number,
default: 12
},
disabled: {
type: Boolean,
default: false
},
action: {
//后台上传接口
type: String,
default: http.baseUrl + '/file/upload'
},
formData: {
//上传所附带数据
type: Object
},
header: {
//自定义头
type: Object,
default() {
return {
Authorization: uni.getStorageSync('Authorization'),
device: uni.getStorageSync('device'),
version: uni.getStorageSync('version')
};
}
},
fileKey: {
//后端接受的filekey
type: String,
default: 'file'
}
},
data() {
return {
isDestroyed: false,
showUploadProgress: true
};
},
mounted: function() {},
destroyed: function() {
this.isDestroyed = true;
},
computed: {},
methods: {
selectImage: function() {
var _self = this;
uni.chooseImage({
count: 1,
sourceType: ['album', 'camera'],
success: function(e) {
var imagePathArr = e.tempFilePaths;
//检查服务器地址是否设置,设置即表示图片要上传到服务器
if (_self.action) {
uni.showToast({
title: '上传中',
icon: 'none',
mask: false
});
var remoteIndexStart = 1 - imagePathArr.length;
var promiseWorkList = [];
var keyname = _self.fileKey ? _self.fileKey : 'upload-images';
var completeImages = 0;
for (let i = 0; i < imagePathArr.length; i++) {
promiseWorkList.push(
new Promise((resolve, reject) => {
let remoteUrlIndex = remoteIndexStart + i;
uni.uploadFile({
url: _self.action,
fileType: 'image',
header: _self.header,
formData: _self.formData,
filePath: imagePathArr[i],
name: keyname,
success: function(res) {
if (res.statusCode === 200) {
if (_self.isDestroyed) {
return;
}
completeImages++;
if (_self.showUploadProgress) {
uni.showToast({
title: '上传中',
icon: 'none',
mask: false,
duration: 500
});
}
// console.log('success to upload image: ' + res.data)
resolve(res.data);
} else {
console.log('fail to upload image:' + res.data);
reject('fail to upload image:' + remoteUrlIndex);
}
},
fail: function(res) {
uni.showToast({
title: '上传失败请检查网络',
icon: 'none'
});
console.log('fail to upload image:' + res);
reject('fail to upload image:' + remoteUrlIndex);
}
});
})
);
}
Promise.all(promiseWorkList).then(result => {
if (_self.isDestroyed) {
return;
}
for (let i = 0; i < result.length; i++) {
var resItem = JSON.parse(result[i]);
_self.$emit('update:modelValue', resItem.data.fullPath);
_self.$emit('change', resItem.data.fullPath);
}
});
} else {
_self.$emit('update:modelValue', '../../static/img/avatar/avatar2.jpg');
_self.$emit('change', '../../static/img/avatar/avatar2.jpg');
}
}
});
}
}
};
</script>
<style scoped>
.zmm-upload-avatar {
}
</style>

View File

@@ -0,0 +1,319 @@
<template>
<view @touchmove.stop.prevent="moveHandle" class="moveWrap">
<movable-area class="movarea" ref="areaBox" id="areaBox" :style="{height:imgSize*rowNum +200+ 'rpx'}">
<view class="imgBox" :style="{height:imgSize*rowNum + 'rpx'}">
<view :id="'img' + idx" class="imgItem" v-for="(item, idx) in imgList" :key="idx"
:style="{transition:addJump?' all 0.5s':'',opacity:idx===selectIdx?'0':'1', width: imgSize + 'rpx', height: imgSize + 'rpx', borderRadius:imgRadius+'rpx',padding:imgPadding+'rpx',left:(hoverImgIdx==='img'+idx?curHoverBoxMove.x+'rpx':''),top:(hoverImgIdx==='img'+idx?curHoverBoxMove.y+'rpx':'')}">
<view class="imgItem-img" @tap="itemclick(item,idx)" @touchstart="tstr(idx, $event)" @touchmove="tsmo" @touchend="toend">
<image v-if="item.videoUrl" class="imgItem-play" src="../../static/img/bf.png" mode="aspectFill"></image>
<image :style="{borderRadius:imgRadius+'rpx' }" :ref="'img' + idx" :src="item.url" mode="aspectFill"></image>
</view>
</view>
<slot></slot>
</view>
<movable-view v-if="moveSrc" :animation="false" class="moveV" :x="x" :y="y" direction="all"
@change="onChange"
:style="{ width: imgSize + 'rpx', height: imgSize + 'rpx',padding:imgPadding+'rpx' }">
<image v-if="moveSrc.videoUrl" class="imgItem-play" src="../../static/img/bf.png" mode="aspectFill"></image>
<image :style="{borderRadius:imgRadius+'rpx' }" :src="moveSrc.url" mode="aspectFill"></image>
</movable-view>
</movable-area>
<view v-if="showDelete">
<view class="delete" :class="{'deleteType':deleteType}">
<text v-if="deleteType">松开删除</text>
<text v-else>拖动到此处删除</text>
</view>
</view>
</view>
</template>
<script>
export default {
emits:['itemclick','update:imgList','deleteImage','change','moveEndList'],
props: {
//图片列表
imgList: {
type: Array,
default: function() {
return [];
}
},
//图片大小
imgSize: {
type: Number
},
//图片间距
imgPadding: {
type: Number
},
//图片行数
rowNum: {
type: Number
},
//图片圆角
imgRadius: {
type: Number
}
},
components: {},
data() {
return {
showDelete:false,
touchobj:null,
deleteType:false,
addJump:false,
areaBoxInfo: {},
x: 0,
y: 0,
selectIdx: null,
moveSrc: null,
areaBoxTop: 0,
hoverImgIdx: '',
inBoxXY: {},
curHoverBoxMove: {
x: 0,
y: 0
}
};
},
watch: {
hoverImgIdx(e) {
let idx = this.selectIdx
let hoverIdx = parseInt(e.split('img')[1]);
if (this.imgList[idx]) {
let selectRow = this.imgList[idx].y / uni.upx2px(this.imgSize)
let selectColum= this.imgList[idx].x / uni.upx2px(this.imgSize)
let hoverRow = this.imgList[hoverIdx].y / uni.upx2px(this.imgSize)
let hoverColum= this.imgList[hoverIdx].x / uni.upx2px(this.imgSize)
let left = -(this.imgSize * (hoverColum - selectColum))
let top= -(this.imgSize * (hoverRow - selectRow))
this.curHoverBoxMove = {
x: left,
y: top,
}
}
},
// imgList(e){
// console.log('变化了item')
// this.$nextTick(()=>{
// this.setImgXy()
// })
// }
imgList: {
deep: true,//深度监听可见听对象中的元素变化例:obj.id
immediate:false,//在进入页面时第一次绑定值不会立刻执行监听只有数据发生改变才会执行handler中的操作
handler(val) {//watch事件
// console.log('变化了item')
this.$nextTick(()=>{
this.setImgXy()
})
}
}
},
methods: {
itemclick(e,i){
this.$emit('itemclick',e,i)
},
moveHandle() {
return;
},
deleteImage: function(e) {
var index = e
var deletedImage = this.imgList[index]
this.imgList.splice(index, 1)
this.$emit('deleteImage', deletedImage)
},
onChange(e) {
var boxHeight=this.imgSize * this.rowNum
var deleteHeight=(boxHeight-80)/2
if(e.detail.y>deleteHeight){
this.deleteType=true
}else{
this.deleteType=false
}
this.showDelete=true
this.$emit('change',e)
},
tstr(e, s) {
this.touchobj=this.imgList[e]
this.addJump=true
this.getDomInfo('areaBox', info => {
this.areaBoxInfo = info;
//获取拖拽区域的上边距和下边距
let areaBoxTop = this.areaBoxInfo.top;
let areaBoxLeft = this.areaBoxInfo.left;
// 设置可移动方块的初始位置为当前所选中图片的位置坐标
this.x = this.imgList[e].x;
this.y = this.imgList[e].y;
//显示可移动方块
this.moveSrc = this.imgList[e];
//保存当前所选择的图片索引
this.selectIdx = e;
var x = s.changedTouches[0].clientX - areaBoxLeft;
var y = s.changedTouches[0].clientY - areaBoxTop;
// 保存鼠标在图片内的坐标
this.inBoxXY = {
x: x - this.imgList[e].x,
y: y - this.imgList[e].y,
}
});
},
tsmo(e) {
let areaBoxTop = this.areaBoxInfo.top;
let areaBoxLeft = this.areaBoxInfo.left;
let imgSize = this.imgSize;
//重置为以拖拽盒子左上角为坐标原点
var x = e.changedTouches[0].clientX - areaBoxLeft;
var y = e.changedTouches[0].clientY - areaBoxTop;
this.x = x - this.inBoxXY.x;
this.y = y - this.inBoxXY.y;
this.imgList.forEach((item, idx) => {
if (x > item.x && x < item.x + imgSize && y > item.y && y < item.y + imgSize) {
this.hoverImgIdx = 'img' + idx
}
});
},
toend(e) {
this.addJump=false
if(this.deleteType){
this.deleteImage(this.selectIdx)
}else{
// 移动结束隐藏可移动方块
let beforeIdx = this.selectIdx;
let afterIdx = parseInt(this.hoverImgIdx.split('img')[1]);
if (this.hoverImgIdx !== '' && beforeIdx !== afterIdx) {
this.imgList[beforeIdx] = this.imgList[afterIdx];
this.imgList[afterIdx] = this.moveSrc;
this.$emit('moveEndList', this.imgList);
}
}
this.moveSrc = '';
this.hoverImgIdx = ''
this.selectIdx = null
this.deleteType=false
this.showDelete=false
this.$nextTick(()=>{
this.setImgXy()
})
},
getDomInfo(id, callBack) {
const query = uni.createSelectorQuery().in(this);
query.select('#' + id)
.boundingClientRect()
.exec(function(res) {
callBack(res[0]);
});
},
setImgXy(){//设置每个图片的基础xy轴
this.getDomInfo('areaBox', info => {
this.areaBoxInfo = info;
// 设置区域内所有图片的左上角坐标
this.imgList.forEach((item, idx) => {
this.getDomInfo('img' + idx, res => {
item.x = res.left - info.left;
});
this.getDomInfo('img' + idx, res => {
item.y = res.top - info.top;
});
});
});
}
},
mounted() {
this.setImgXy()
}
};
</script>
<style lang="scss" scoped>
.imgItem-play{
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 80rpx !important;
height: 80rpx !important;
transform: translate(-50%,-50%);
}
.movarea {
width: 100%;
height: 320rpx;
display: flex;
flex-direction: row;
}
.imgBox {
position: relative;
z-index: 1;
width: 100%;
height: 320rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
.imgItem {
position: relative;
box-sizing: border-box;
left: 0;
top: 0;
box-sizing: border-box;
image {
width: 100%;
height: 100%;
// transition: all 0.2s;
vertical-align: top;
}
}
}
.moveV {
opacity: 0.6;
z-index: 0;
box-sizing: border-box;
image {
width: 100%;
height: 100%;
}
}
.select {
opacity: 0;
}
.zmm-upload-image-deleteIcon {
right: 0rpx;
top: 0rpx;
position: absolute;
background-color: rgba(0, 0, 0, 0.3);
width: 36rpx;
height: 36rpx;
text-align: center;
border-radius: 50%;
color: white;
font-size: 30rpx;
z-index: 2;
display: flex;flex-direction: row;align-items: center;justify-content: center;
}
.delete{
position: absolute;
bottom: 0rpx;
left: 0;
width: 100%;
height: 80rpx;
line-height: 80rpx;
background: #C04A42;
text-align: center;
transition: all 0.3s;
}
.delete text{
color: #fff;
}
.deleteType{
transform: scale(1.1);
}
.moveWrap{
position: relative;
}
.imgItem-img{
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,437 @@
<template>
<view class="zmm-upload-image" v-if="show">
<zmm-upload-image-drag :key="key" :rowNum="rows" :imgRadius="imgRadius" :imgSize="imgSize" :imgPadding="imgPadding" :imgList="fileList" @deleteImage="deleteImage" @itemclick="itemclick">
<view v-if="showAdd && fileList.length < limit" @tap="chooseTap" :style="{ width: imgSize + 'rpx', height: imgSize + 'rpx',padding:imgPadding+'rpx' }" class="zmm-upload-image-item-slot">
<view class="zmm-upload-image-item-slotIcon">
<uni-icons type="plusempty" color="#6E6E6E" size="24" />
</view>
</view>
</zmm-upload-image-drag>
</view>
</template>
<script>
import zmmUploadImageDrag from './zmm-upload-image-drag.vue';
import http from '@/common/request.js';
import customHttp from "@/common/customHttp.js"
import config from "@/common/config.js"
export default {
components: {
zmmUploadImageDrag
},
name: 'zmm-upload-image',
emits: ['update:modelValue', 'deleteImage','allComplete','oneComplete'],
props: {
modelValue: {
type: [Array]
},
chooseType:{
type:String,
default:'chooseImage'//chooseImage图片 chooseVideo视频 chooseMedia图片或视频
},
imgSize: {
//图片大小
type: Number,
default: 214
},
imgPadding: {
//图片间距
type: Number,
default: 6
},
imgRadius: {
//图片圆角
type: Number,
default: 4
},
fileList: {
//图片数据
type: Array,
default(){
return []
}
},
rowNumber: {
//一行多少个图片
type: Number,
default: 3
},
showAdd: {
//增加按钮
type: Boolean,
default: true
},
show: {
//是否显示
type: Boolean,
default: true
},
fileAction: {
//后台文件上传接口
type: String,
default: config.getImApiUrl() + '/file/upload'
},
videoAction: {
//后台上传接口
type: String,
default: config.getImApiUrl() + '/file/uploadVideo'
},
formData: {
//上传所附带数据
type: Object
},
header: {
//自定义头
type: Object,
default() {
return {
Authorization: uni.getStorageSync('Authorization'),
device: uni.getStorageSync('device'),
version: uni.getStorageSync('version')
};
}
},
limit: {
//图片数量
type: Number,
default: 9
},
fileKey: {
//后端接受的filekey
type: String,
default: 'file'
}
},
data() {
return {
key:0,
isDestroyed: false,
showUploadProgress:true
};
},
watch: {
modelValue(val) {
console.log(val);
},
fileList(e){
// console.log('变化了list')
this.key++
},
rows(e){
this.key++
}
// fileList: {
// deep: true,//深度监听可见听对象中的元素变化例:obj.id
// immediate:false,//在进入页面时第一次绑定值不会立刻执行监听只有数据发生改变才会执行handler中的操作
// handler(val) {//watch事件
// console.log('变化了list')
// this.key++
// }
// }
},
mounted: function() {},
destroyed: function() {
this.isDestroyed = true;
},
computed: {
rows() {
var buzhu=this.showAdd && this.fileList.length < this.limit ? 1 : 0;
var rows=Math.ceil((this.fileList.length+buzhu) / this.rowNumber);
return rows
}
},
methods: {
itemclick(e,i){
if(!e.videoUrl){
var imgs=[]
for (var i = 0; i < this.fileList.length; i++) {
var item=this.fileList[i]
if(!item.videoUrl){
imgs.push(item.url)
}
}
this.previewImage(imgs,i)
}else{
this.$fc.plusDownload({onlinePath:e.videoUrl}).then(res=>{
this.$fc.plusOpenFile({filePath:res})
})
}
},
chooseTap(){
switch (this.chooseType){
case 'chooseImage':
this.selectImage()
break;
case 'chooseVideo':
this.selectVideo()
break;
case 'chooseMedia':
uni.showActionSheet({
itemList: ['图片'],
// itemList: ['图片', '视频'],
success: (res) => {
switch (res.tapIndex){
case 0:
this.selectImage()
break;
case 1:
this.selectVideo()
break;
default:
break;
}
}
});
break;
default:
break;
}
},
selectImage: function() {
var _self = this;
if (!_self.fileList) {
_self.fileList = [];
}
uni.chooseImage({
count: _self.limit ? _self.limit - _self.fileList.length : 999,
sourceType: ['album', 'camera'],
sizeType:['original','compressed'],
success: function(e) {
var imagePathArr = e.tempFilePaths;
//如果设置了limit限制在web上count参数无效这里做判断控制选择的数量是否合要求
//在非微信小程序里,虽然可以选多张,但选择的结果会被截掉
//在app里会自动做选择数量的限制
if (_self.limit) {
var availableImageNumber = _self.limit - _self.fileList.length;
if (availableImageNumber < imagePathArr.length) {
uni.showToast({
title: '图片总数限制为' + _self.limit + '张,当前还可以选' + availableImageNumber + '张',
icon: 'none',
mask: false,
duration: 2000
});
return;
}
}
//检查服务器地址是否设置,设置即表示图片要上传到服务器
if (_self.fileAction) {
uni.showToast({
title: '上传进度0/' + imagePathArr.length,
icon: 'none',
mask: false
});
var remoteIndexStart = _self.fileList.length - imagePathArr.length;
var promiseWorkList = [];
var keyname = _self.fileKey ? _self.fileKey : 'upload-images';
var completeImages = 0;
for (let i = 0; i < imagePathArr.length; i++) {
promiseWorkList.push(
new Promise((resolve, reject) => {
let remoteUrlIndex = remoteIndexStart + i;
uni.uploadFile({
url: _self.fileAction,
fileType: 'image',
header: _self.header,
formData: _self.formData,
filePath: imagePathArr[i],
name: keyname,
success: function(res) {
if (res.statusCode === 200) {
if (_self.isDestroyed) {
return;
}
completeImages++;
if (_self.showUploadProgress) {
uni.showToast({
title: '上传进度:' + completeImages + '/' + imagePathArr.length,
icon: 'none',
mask: false,
duration: 500
});
}
// console.log('success to upload image: ' + res.data)
var resItem=JSON.parse(res.data)
console.error(resItem)
_self.$emit('oneComplete', {
name: resItem.data,
url: resItem.data,
type:'IMAGE'
},_self.chooseType);
resolve(res.data);
} else {
console.log('fail to upload image:' + res.data);
reject('fail to upload image:' + remoteUrlIndex);
}
},
fail: function(res) {
uni.showToast({
title: '上传失败请检查网络',
icon: 'none'
});
console.log('fail to upload image:' + res);
reject('fail to upload image:' + remoteUrlIndex);
}
});
})
);
}
Promise.all(promiseWorkList).then(result => {
if (_self.isDestroyed) {
return;
}
var fresult=[]
for (let i = 0; i < result.length; i++) {
var resItem=JSON.parse(result[i])
_self.fileList.push({
name: resItem.data.fileName,
url: resItem.data.fullPath,
type:'IMAGE'
});
fresult.push({
name: resItem.data.fileName,
url: resItem.data.fullPath,
type:'IMAGE'
})
}
_self.$emit('allComplete', fresult,_self.chooseType);
});
} else {
var testImg='图片地址'
for (let i = 0; i < imagePathArr.length; i++) {
_self.fileList.push({
name: 'avatar'+i+'.jpg',
url: testImg
});
}
}
}
});
},
selectVideo: function() {
var _self = this;
if (!_self.fileList) {
_self.fileList = [];
}
uni.chooseVideo({
sourceType: ['album', 'camera'],
compressed:true,
success: function(e) {
if (_self.showUploadProgress) {
uni.showLoading({
title:'上传中...'
})
}
uploadVideo(e).then(res=>{
uni.hideLoading()
_self.fileList.push(res);
_self.$emit('allComplete', res ,_self.chooseType);
})
function uploadVideo(e){
return new Promise((resolve, reject) => {
uni.uploadFile({
url: _self.videoAction,
fileType: 'video',
header: _self.header,
formData: _self.formData,
filePath: e.tempFilePath,
name: _self.fileKey,
success: function(res) {
if (res.statusCode === 200) {
if (_self.isDestroyed) {
return;
}
var resItem=JSON.parse(res.data)
var fresult={
name: resItem.data.fileName,
videoUrl: resItem.data.fullPath,
url: resItem.data.screenShot,
type:'VIDEO'
}
_self.$emit('oneComplete', {
name: resItem.data.fileName,
videoUrl: resItem.data.fullPath,
url: resItem.data.screenShot,
type:'VIDEO'
},_self.chooseType);
resolve(fresult)
} else {
reject(res)
}
},
fail: function(res) {
uni.showToast({
title: '上传失败请检查网络',
icon: 'none'
});
reject(res)
}
});
})
}
}
});
},
deleteImage: function(e) {
this.$emit('deleteImage', e);
},
previewImage: function(arr,index) {
uni.previewImage({
current: index,
indicator: 'number',
loop: true,
urls: arr
});
},
}
};
</script>
<style scoped>
.zmm-upload-image {
width: 100%;
position: relative;
/* display: flex;flex-direction: row;justify-content: center; */
}
.zmm-upload-image-list {
/* width: 714rpx; */
display: flex;
flex-wrap: wrap;
flex-direction: row;
}
.zmm-upload-image-item {
width: 214rpx;
height: 214rpx;
margin: 12rpx;
box-sizing: border-box;
position: relative;
border-radius: 4rpx;
}
.zmm-upload-image-item-img {
width: 100%;
height: 100%;
border-radius: 4rpx;
}
.zmm-upload-image-item-slot{
box-sizing: border-box;
}
.zmm-upload-image-item-slotIcon {
box-sizing: border-box;
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
background-color: #f7f7f7;
}
</style>

View File

@@ -0,0 +1,568 @@
<template name="zy-search">
<view class="good-search-page" :class="Theme">
<view class="good-search-head">
<input :class="{'clearvalue':clearValuebtn}" :maxlength="maxLength" :style="{'line-height': inputHeight+'px','height': inputHeight+'px'}" :focus="isFocus" type="text" confirm-type="search" @confirm="searchStart()" :placeholder="inputPlaceholder" :confirm-hold="true" v-model="searchText" />
<!-- <view v-if="searchText.length>0&&clearValuebtn" class="good-search-icon clearvalue-icon iconfont iconshanchu1" @click="clearvalue()"></view> -->
<view class="good-search-icon search-icon2" @click="goback()">取消</view>
<view class="good-search-icon search-icon iconfont iconsousuo-copy" @click="searchStart()"></view>
<scroll-view class="autocomplay" :style="{'top': inputHeight+5+'px'}" v-if="autocomplaystate" scroll-y>
<view class="autocomplay-item" v-for="(item,index) in autocomplayarr" :key="index" @click="tagsClick(item.value)">
<rich-text :nodes="item.richtxt"></rich-text>
</view>
<view style="color: #999;text-align: center;justify-content: center; line-height: 70rpx;" v-if="autocomplayarr.length<1">没有相关信息</view>
</scroll-view>
</view>
<view class="good-search-body">
<view class="search-history" v-if="hList.length > 0">
<view class="header">
<text>历史记录</text>
<view class="good-search-icon delete-icon iconfont iconshanchu" @click="delhistory()"></view>
</view>
<view class="list">
<view v-for="(item,index) in hList" :key="index" @click="tagsClick(item)">{{item}}</view>
</view>
</view>
<view class="search-showhot" v-if="hotList.length>0">
<view class="header">
<text>猜你想搜的</text>
</view>
<view class="list">
<view v-for="(item,index) in hotList" :key="index" @click="tagsClick(item)">{{item}}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
clearValuebtn: { //是否显示清空按钮
type: Boolean,
default: true
},
isFocus: { //是否自动获取焦点
type: Boolean,
default: false
},
Theme: { //选择主题class
type: String,
default: 'good-search-circle'
},
maxLength: { //字符最大长度
type: [String, Number],
default: '140'
},
inputHeight: { //搜索框高度单位px
type: [String, Number],
default: '35'
},
inputPlaceholder: { //搜索框默认提示
type: String,
default: '请输入关键词搜索'
},
autoComplaylist: { //自动联想数据
type: Array,
default () {
return []
}
},
historyNum: { //历史记录保存数量
type: Number,
default: 6
},
hotList: { //推荐列表数据
type: Array,
default () {
return []
}
},
speechEngine: { //语音引擎=>讯飞:iFly,百度:'baidu'
type: String,
default: 'iFly'
},
Punctuation: { //是否开启语音识别标点符号
type: Boolean,
default: false
},
callbackTime: { //input回调缓冲时间(不返回500毫秒以内输入的数据防止每输入一个值就会触发一次)
type: Number,
default: 500
},
},
data() {
return {
speechengine: this.speechEngine,
punctuation: this.Punctuation,
historynum: this.historyNum,
callbacktime: this.callbackTime,
autocomplaylist: this.getcomplaylist(this.autoComplaylist),
autocomplayarr: [],
autocomplaystate: false,
searchText: '',
hList: uni.getStorageSync('search_cache'),
};
},
watch: {
historyNum: function(val) {
this.historynum = val;
},
speechEngine: function(val) {
this.engine = val;
},
hotList: function(val) {
this.hotList = val;
},
autoComplaylist: function(val) {
this.autocomplaylist = this.getcomplaylist(val);
if (!this.searchText || this.searchText == '') {
this.autocomplaystate = false
return
}
this.autocomplayarr = this.replacekeyword(this.autocomplaylist, this.searchText)
this.autocomplaystate = this.autocomplayarr.length > 0 ? true : false;
},
searchText: function(val) {
let _this = this;
var searchWords = val.replace(/^ +| +$/g, '');
if (!searchWords || searchWords == '') {
this.autocomplaystate = false
return
}
if (this.calbacktime) {
clearTimeout(_this.calbacktime)
}
this.calbacktime = setTimeout(function() {
_this.inputChange(searchWords)
}, _this.callbackTime);
this.autocomplayarr = this.replacekeyword(this.autocomplaylist, searchWords)
this.autocomplaystate = this.autocomplayarr.length > 0 ? true : false;
},
},
methods: {
goback(){
uni.navigateBack({
delta:1
})
},
clearvalue() { //删除input值
let _this = this;
setTimeout(function() { //增加延时解决键盘收回时导致的@input事件
_this.searchText = ''
}, 20);
this.searchText = ''
},
getcomplaylist(arr) { //初始化自动联想数组
var data = []
for (var i = 0; i < arr.length; i++) {
data.push({
richtxt: arr[i],
value: arr[i]
})
}
return data
},
replacekeyword(arr, searchWords) { //返回符合关键词的高亮数组
var data = []
for (var i = 0; i < arr.length; i++) {
if (arr[i].richtxt.search(searchWords) != -1) {
data.push({
richtxt: arr[i].richtxt.replace(searchWords, "<span style='color: #333;font-weight:bold'>" + searchWords +
"</span>"),
value: arr[i].value
})
}
}
return data
},
tagsClick(item) { //标签点击事件
let _this = this;
setTimeout(function() { //增加延时解决键盘收回时导致的@input事件
_this.searchText = item
_this.searchStart()
}, 20);
this.$emit('tagsClick', item)
},
inputChange(e) { //input回调
this.$emit('inputChange', e)
},
notSupport() { //不支持提醒
uni.showToast({
title: '该平台暂不支持',
icon: 'none',
duration: 1000
});
},
searchStart() { //触发搜索
let _this = this;
if (_this.searchText == '') {
uni.showToast({
title: '请输入关键字',
icon: 'none',
duration: 1000
});
return false;
}
// else {
// if(this.autocomplayarr.length<1){
// this.autocomplaystate=true
// return
// }
// uni.getStorage({
// key: 'search_cache',
// success(res) {
// let list = res.data;
// if (list.length >= _this.historynum) {
// for (let item of list) {
// if (item == _this.searchText) {
// return false;
// }
// }
// list.pop();
// list.unshift(_this.searchText);
// } else {
// for (let item of list) {
// if (item == _this.searchText) {
// return false;
// }
// }
// list.unshift(_this.searchText);
// }
// _this.hList = list;
// uni.setStorage({
// key: 'search_cache',
// data: _this.hList
// });
// },
// fail() {
// _this.hList = [];
// _this.hList.push(_this.searchText);
// uni.setStorage({
// key: 'search_cache',
// data: _this.hList
// });
// }
// })
// }
this.$emit('clickSearch', _this.searchText)
},
delhistory() { //清空历史记录
this.hList = [];
uni.setStorage({
key: 'search_cache',
data: []
});
this.$emit('delhHistory')
},
startRecognize() { //语音输入
let _this = this;
let options = {};
options.engine = _this.speechengine;
options.punctuation = _this.punctuation; // 是否需要标点符号
options.timeout = 1000;
plus.speech.startRecognize(options, function(s) {
_this.searchText = _this.searchText + s;
});
}
}
}
</script>
<style scoped>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAARsAAsAAAAACTAAAAQeAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDMgqFOIR/ATYCJAMUCwwABCAFhG0HXhshCBEVnP3IfiTGtg+yYk9cckRpaoLn+Ty/c9976WTSnw8NxB3gNgOIO3pXjJjRVmgD9Ab4PpuONYw5YTol8dSz/I/7dt/vpPE4jVIIiZD8//ctbT0e2HotiyTD+1qXxepTJ4rjgALadwoja4DGBsTHZGxxVg7MnJ/5HAI4FKQ80rBxy47YGGidADJs8MDe2B0bZoEj2MbAxFGDrMLCVovVA2Bl8fPyhapig8LS0Na2Axr1o66P31LPSsyiW0ZDfjgPYJ0CDZQHDMiYSd8wdJUrj8YJfs9QF3CwUSgfP9sv7bdMJCBU3ZDBkT5QN/OPp0ECkC4J7Lp5Az6JCAr8bBEE/NKyZX1L2NA60zI4Byq8AakDdnGd+mjadOnkCgUCQZe2JBWQrrOPLDlENBrCKQ7NOhx0lx6dfcRxJB73ZLxDIpGOsVjY60kZ8igzOXw9LgUBGelc6m3cuuVAZsRx5GvKIpG66zZnkSW0fl+aqQyrs2/PC4MqGm3k3o57OnjwGM/GYh13Xl76sMCgA5cKaldtu1A8yVt6VE6qgY0tiXyypWtSP/W45FhtsVi3dUuywx3Tq47MzuWFN8w+ujQr5G1cenhWZsfQenHrt0Nual5cNWf/2RnJgwa45BXXcaVvHjDIwtCj5mC0pbqoUevXj0rVykS01XygQmPGhkWi1q5CD1V4zJiwAgLTK6ixKmnjng+VFzQ3pz5/PqlPaRCoT+KaU6ZLu+LnFx6adKNhoZbr9a1bap1EeNbrdQ2mn6ZhlWmvN9uLq2TsP7U4b5W8FB5bv7F076IbRQtPLDExo9Ibadiy2+o5q1t1pdHrcsP37+hG3bvu21zld1OnSNq6uuWtV79fm/JtF9YYl+plfM3wiugNm1M99VW1huenTU6hsK5/2fz9ay5L1EmqairU5E15Nq0kzL1F8ywu6YspBjsP5znMJAAS1011/YKXvq7rYabb2/VcX9P+QgCB5CWns3um1PxmBw0AT9ymU0Q0kYEv+0/oCeR/Hjmzz4RKslvG701kftwixF9Dp4KSAyQQSufFYQxZ7EYQbLIaUOSgAGhsitMNuzxYBKkOAWzqgEM5PdXpIBlYa8TkBMryFiCk8gFFMpdBk8qXbtg/sMjmDwFSZQ0O4WVcGaSktKcGFplHCXyGVtCklNOfyuYb1mFgtq0suC+0rk1Ckeb9YoUT2kPscLEuvVegLI1wFa/hMBBoSx0Knzbe60uWqakPpYLGxYkGLMR4SAK4GdASyERlo73T4Ps3qBYMGLtAGYi/IMtpJwcKUnIGvdJNLMpLme1EtRLPU/oqxSIj4AoPGmSOAHp6UQcJXkqzQ0a7kGmDFFeXnt4wvsdjgAPNtkSJFiOWBEi7b3fydkLpGqZRkJ7PYym4QEmj2U8UJtGEQ+EtFgsAAA==') format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconcombinedshapecopy:before {
content: "\e616";
}
.iconsousuo-copy:before {
content: "\e600";
}
.iconshanchu:before {
content: "\e64c";
}
.iconshanchu1:before {
content: "\e628";
}
page {
display: flex;
flex-direction: column;
box-sizing: border-box;
background-color: #F5F5F5;
}
view {
display: flex;
font-size: 28upx;
line-height: inherit
}
.good-search-page {
box-sizing: border-box;
width: 750rpx;
padding: 30rpx;
display: flex;
flex-direction: column;
}
.good-search-head {
position: relative;
}
.good-search-head input {
box-sizing: border-box;
width: 100%;
background-color: #F7F7F7;
font-size: 28rpx;
border-radius: 50rpx;
padding: 0 73rpx;
padding-left: 34rpx;
padding-right: 160rpx;
}
.good-search-head input.clearvalue{
}
.good-search-icon {
color: #888;
justify-content: center;
align-items: center;
font-size: 38rpx;
}
.good-search-head .good-search-icon {
width: 73rpx;
height: 100%;
position: absolute;
z-index: 2;
top: 0rpx;
}
.good-search-head .voice-icon {
left: 0;
}
.good-search-head .search-icon {
width: 63rpx;
right: 97rpx;
}
.good-search-head .search-icon2 {
font-size: 28rpx;
right: 0;
width: 73rpx;
padding-right: 24rpx;
}
.good-search-head .clearvalue-icon {
width: auto;
right: 146rpx;
color: #ccc;
}
.good-search-head .autocomplay {
left: 0;
top: 80rpx;
border-radius: 20rpx;
height: 70vh;
background: #f7f7f7;
position: absolute;
z-index: 9;
}
.good-search-head .autocomplay .autocomplay-item {
padding: 0 24rpx;
line-height: 70rpx;
height: 70rpx;
border-bottom: 1px #eee solid;
color: #999;
}
.good-search-body {
display: flex;
flex-direction: column;
}
.good-search-page .search-history {
display: flex;
flex-direction: column;
}
.good-search-page .search-showhot {
display: flex;
flex-direction: column;
}
/* 主题1 */
.good-search-rect {}
.good-search-rect .good-search-head input {
border-radius: 0;
}
.good-search-rect .good-search-head input.clearvalue{
}
.good-search-rect .good-search-head .voice-icon {}
.good-search-rect .good-search-head .search-icon {}
.good-search-rect .good-search-head .clearvalue-icon {}
.good-search-rect .header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 24rpx 0;
padding: 0 12rpx;
}
.good-search-rect .header text {
color: #666;
font-weight: bold;
font-size: 32rpx;
margin-right: auto;
}
.good-search-rect .list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.good-search-rect .list view {
display: block;
width: 49%;
color: #8A8A8A;
font-size: 28rpx;
box-sizing: border-box;
text-align: center;
padding: 20rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
background-color: #F7F7F7;
margin: 0.5%;
}
.good-search-rect .search-showhot {}
/* 主题2 */
.good-search-circle {}
.good-search-circle .good-search-head input {
border-radius: 50rpx;
}
.good-search-circle .good-search-head input.clearvalue{
}
.good-search-circle .good-search-head .voice-icon {}
.good-search-circle .good-search-head .search-icon {}
.good-search-circle .good-search-head .clearvalue-icon {}
.good-search-circle .header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 24rpx 0;
padding: 0 12rpx;
}
.good-search-circle .header text {
color: #666;
font-weight: bold;
font-size: 32rpx;
margin-right: auto;
}
.good-search-circle .list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.good-search-circle .list view {
display: block;
padding: 8rpx 18rpx;
margin: 12rpx;
margin-top: 0;
font-size: 28rpx;
color: #8A8A8A;
background-color: #F7F7F7;
box-sizing: border-box;
text-align: center;
border-radius: 20rpx;
}
.good-search-circle .search-showhot {}
/* 主题3 */
.good-search-tb {}
.good-search-tb .good-search-head input {
border-radius: 0;
}
.good-search-tb .good-search-head input.clearvalue{
padding-right: 133rpx;
}
.good-search-tb .good-search-head .voice-icon {}
.good-search-tb .good-search-head .search-icon {
background: #FF9A33;
color: #fff;
}
.good-search-tb .good-search-head .clearvalue-icon {
width: 60rpx;
}
.good-search-tb .header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 24rpx 0;
padding: 0 12rpx;
}
.good-search-tb .header text {
color: #666;
font-weight: bold;
font-size: 32rpx;
margin-right: auto;
}
.good-search-tb .list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.good-search-tb .list view {
display: block;
padding: 8rpx 30rpx;
margin: 12rpx;
margin-top: 0;
font-size: 28rpx;
color: #8A8A8A;
background-color: #F7F7F7;
box-sizing: border-box;
text-align: center;
border-radius: 20rpx;
}
.good-search-tb .search-showhot {}
</style>

18
im-uniapp/index.html Normal file
View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="text/javascript" src="https://172.16.3.18/static_file/vconsole.min.js"></script>
<script>
var vConsole = new VConsole();
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

Binary file not shown.

69
im-uniapp/main.js Normal file
View File

@@ -0,0 +1,69 @@
import http from '@/common/request'
import store from './store'
import md5 from "@/common/md5.js";
import publicFc from "@/common/publicFc.js";
// #ifdef H5
import socketTask from "@/common/socketTask.js";
// #endif
import zmmFormCheck from './common/zmmFormCheck.js';
import pinyin from './common/pinyin.js';
// #ifndef VUE3
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
Vue.prototype.$socketTask = socketTask;
Vue.prototype.$http = http
Vue.prototype.$store = store
Vue.prototype.$fc = publicFc;
Vue.prototype.$md5 = md5
Vue.prototype.$zmmFormCheck = zmmFormCheck;
Vue.prototype.$pinyin = pinyin;
// #ifdef H5
// uni.lim = lim;
// Vue.prototype.imsdk = lim
// #endif
App.mpType = 'app'
const app = new Vue({
...App,
store
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
import App from './App.vue'
export function createApp() {
const app = createSSRApp(App)
app.config.globalProperties.$socketTask = socketTask
app.config.globalProperties.$http = http
app.config.globalProperties.$fc = publicFc
app.config.globalProperties.$md5 = md5
app.config.globalProperties.$zmmFormCheck = zmmFormCheck
app.config.globalProperties.$pinyin = pinyin
// #ifdef H5
// #endif
app.use(store);
return {
app
}
}
// #endif

224
im-uniapp/manifest.json Normal file
View File

@@ -0,0 +1,224 @@
{
"name" : "IM",
"appid" : "__UNI__1A208D0",
"description" : "HS-IM",
"versionName" : "1.2.1",
"versionCode" : 121,
"developer" : {
"name" : "zmm2113@qq.com",
"email" : "zmm2113@qq.com"
},
"transformPx" : false,
"app-plus" : {
/* 5+App */
"permissions" : {
"File" : {
"description" : "文件系统"
}
},
"usingComponents" : true,
"nvueCompiler" : "uni-app",
"splashscreen" : {
"alwaysShowBeforeRender" : false,
"waiting" : false,
"autoclose" : false,
"delay" : 0
},
"modules" : {
"Geolocation" : {},
"Maps" : {},
"Push" : {},
"Speech" : {},
"VideoPlayer" : {},
"iBeacon" : {},
"Barcode" : {},
"Camera" : {}
},
/* */
"distribute" : {
/* */
"android" : {
/* android */
"permissions" : [
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.BLUETOOTH\"/>",
"<uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.BLUETOOTH\"/>",
"<uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\"/>"
],
"minSdkVersion" : 21
},
"ios" : {
"dSYMs" : false
},
/* ios */
"sdkConfigs" : {
"geolocation" : {
"system" : {
"__platform__" : [ "ios", "android" ]
},
"amap" : {
"__platform__" : [ "ios", "android" ],
"appkey_ios" : "81cc6c72aeb6a1946510cc1e9f87ee80",
"appkey_android" : "81cc6c72aeb6a1946510cc1e9f87ee80"
}
},
"maps" : {
"amap" : {
"appkey_ios" : "81cc6c72aeb6a1946510cc1e9f87ee80",
"appkey_android" : "81cc6c72aeb6a1946510cc1e9f87ee80"
}
},
"push" : {
"unipush" : {}
},
"speech" : {
"ifly" : {}
},
"ad" : {}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
},
"splashscreen" : {
"androidStyle" : "default",
"android" : {
"hdpi" : "unpackage/res/splash/splash480.9.png",
"xhdpi" : "unpackage/res/splash/splash750.9.png",
"xxhdpi" : "unpackage/res/splash/splash1080.9.png"
}
}
},
"uniStatistics" : {
"enable" : true
},
"nativePlugins" : {
"TUICallingUniPlugin-TUICallingModule" : {
"__plugin_info__" : {
"name" : "TUICallingUniPlugin-TUICallingModule",
"description" : "腾讯云音视频插件",
"platforms" : "Android,iOS",
"url" : "",
"android_package_name" : "",
"ios_bundle_id" : "",
"isCloud" : false,
"bought" : -1,
"pid" : "",
"parameters" : {}
}
}
}
},
/* SDK */
"quickapp" : {},
/* */
"mp-weixin" : {
/* */
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true,
"uniStatistics" : {
"enable" : true
}
},
"vueVersion" : "3",
"uniStatistics" : {
"enable" : true,
"version" : "1"
},
"h5" : {
"uniStatistics" : {
"enable" : true
},
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "3U7BZ-AZZKD-IXC4C-HZA7T-2PGKT-EZFER"
}
}
},
"router" : {
"base" : "/im-uniapp/",
"mode" : "history"
},
"devServer" : {
"https" : false
}
},
"mp-alipay" : {
"uniStatistics" : {
"enable" : true
}
},
"mp-baidu" : {
"uniStatistics" : {
"enable" : true
}
},
"mp-kuaishou" : {
"uniStatistics" : {
"enable" : true
}
},
"mp-lark" : {
"uniStatistics" : {
"enable" : true
}
},
"mp-qq" : {
"uniStatistics" : {
"enable" : true
}
},
"mp-toutiao" : {
"uniStatistics" : {
"enable" : true
}
}
}

21
im-uniapp/package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "hs-im",
"version": "1.1.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git"
},
"author": "php_echo@163.com",
"license": "ISC",
"dependencies": {
"base64-js": "^1.5.1",
"dayjs": "^1.11.10",
"js-base64": "^3.7.5",
"localforage": "^1.10.0",
"luch-request": "^3.1.1"
}
}

718
im-uniapp/pages.json Normal file
View File

@@ -0,0 +1,718 @@
{
"pages": [
// {
// "path": "wx/tabbar1/index",
// "name": "wxtabbar1",
// "aliasPath": "/wxtabbar1",
// "requireAuth": true,
// "style": {
// "navigationBarTitleText": "消息",
// "app-plus": {
// "titleNView": {
// "buttons": [{
// "text": "\ue657",
// "fontSrc": "/static/wx_iconfont.ttf",
// "fontSize": "28px"
// }, {
// "text": "\ue636",
// "fontSrc": "/static/wx_iconfont.ttf",
// "fontSize": "26px"
// }]
// }
// }
// }
// },
{
"path": "pages/wxindex/index",
"name": "wxindex",
"aliasPath": "/wxindex",
"requireAuth": true,
"style": {
"navigationBarTitleText": "微信首页",
"navigationStyle": "custom"
}
}, {
"path": "pages/agreement/index",
"name": "agreement",
"aliasPath": "/agreement",
"requireAuth": true,
"style": {
"navigationBarTitleText": "隐私及服务协议",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8",
"app-plus": {
"background": "#efeff4"
}
},
"tabBar": { //底部
"color": "#999999",
"selectedColor": "#09C160",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "wx/tabbar1/index",
"iconPath": "static/wx/n0.png",
"selectedIconPath": "static/wx/n0_on.png",
"text": "消息"
},
{
"pagePath": "wx/tabbar2/index",
"iconPath": "static/wx/n01.png",
"selectedIconPath": "static/wx/n01_on.png",
"text": "通讯录"
}
]
},
"subPackages": [ //分包微信小程序单包最大2M
{
"root": "wx",
"pages": [{
"path": "login/index",
"name": "wxlogin",
"aliasPath": "/wxlogin",
"requireAuth": false,
"style": {
"navigationBarTitleText": "登录"
}
}, {
"path": "register/index",
"name": "wxregister",
"aliasPath": "/wxregister",
"requireAuth": false,
"style": {
"navigationBarTitleText": "注册"
}
}, {
"path": "forgetPass/index",
"name": "wxforgetPass",
"aliasPath": "/wxforgetPass",
"requireAuth": false,
"style": {
"navigationBarTitleText": "忘记密码"
}
}, {
"path": "chatWindow/index",
"name": "wxchatWindow",
"aliasPath": "/wxchatWindow",
"requireAuth": true,
"style": {
"navigationBarTitleText": "聊天窗口",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "\ue623",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "28px"
}]
}
}
}
}
, {
"path": "tabbar1/index",
"name": "wxtabbar1",
"aliasPath": "/wxtabbar1",
"requireAuth": true,
"style": {
"navigationBarTitleText": "消息"
}
}
, {
"path": "tabbar2/index",
"name": "wxtabbar2",
"aliasPath": "/wxtabbar2",
"requireAuth": true,
"style": {
"navigationBarTitleText": "通讯录"
}
}, {
"path": "tabbar3/index",
"name": "wxtabbar3",
"aliasPath": "/wxtabbar3",
"requireAuth": true,
"style": {
"navigationBarTitleText": "发现",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "\ue657",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "28px"
}, {
"text": "\ue636",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "26px"
}]
}
}
}
}, {
"path": "tabbar4/index",
"name": "wxtabbar4",
"aliasPath": "/wxtabbar4",
"requireAuth": true,
"style": {
"navigationBarTitleText": "我",
"navigationBarBackgroundColor": "#ffffff"
}
}, {
"path": "group/createGroup",
"name": "wxcreateGroup",
"aliasPath": "/wxcreateGroup",
"requireAuth": true,
"style": {
"navigationBarTitleText": "发起群聊"
}
}, {
"path": "shake/index",
"name": "wxshake",
"aliasPath": "/wxshake",
"requireAuth": true,
"style": {
"navigationBarTitleText": "摇一摇",
"navigationBarBackgroundColor":"#212121",
"navigationBarTextStyle":"white"
}
}, {
"path": "nearby/index",
"name": "wxnearby",
"aliasPath": "/wxnearby",
"requireAuth": true,
"style": {
"navigationBarTitleText": "附近的人",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "\ue623",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "28px"
}]
}
}
}
}, {
"path": "friendsCircle/msglist",
"name": "wxfriendsCirclemsglist",
"aliasPath": "/wxfriendsCirclemsglist",
"requireAuth": true,
"style": {
"navigationBarTitleText": "消息",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "清空",
"fontSize": "16px",
"width":"44px",
"fontWeight":"bold"
}]
}
}
}
}, {
"path": "system/index",
"name": "wxsystem",
"aliasPath": "/wxsystem",
"requireAuth": true,
"style": {
"navigationBarTitleText": "设置"
}
}, {
"path": "search/index",
"name": "wxsearch",
"aliasPath": "/wxsearch",
"requireAuth": true,
"style": {
"navigationBarTitleText": "搜索"
}
}, {
"path": "search-friends/index",
"name": "wxsearch-friends",
"aliasPath": "/wxsearch-friends",
"requireAuth": true,
"style": {
"navigationBarTitleText": "新的朋友",
"enablePullDownRefresh" : true,
"app-plus": {
"titleNView": {
"buttons": [{
"text": "添加",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "search-friends/add",
"name": "wxsearch-friends-add",
"aliasPath": "/search-friends-add",
"requireAuth": true,
"style": {
"navigationBarTitleText": "朋友验证",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "发送",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "personDetail/index",
"name": "wxpersonDetail",
"aliasPath": "/wxpersonDetail",
"requireAuth": true,
"style": {
"navigationBarTitleText": "个人信息" //个人中心信息
}
}, {
"path": "personInfo/addFriendsDetail",
"name": "wxpersonInfoAddFriendsDetail",
"aliasPath": "/wxpersonInfoAddFriendsDetail",
"requireAuth": true,
"style": {
"navigationBarTitleText": "" //添加好友信息
}
}, {
"path": "personInfo/beizhu",
"name": "wxpersonInfobeizhu",
"aliasPath": "/wxpersonInfobeizhu",
"requireAuth": true,
"style": {
"navigationBarTitleText": "设置备注", //修改备注
"app-plus": {
"titleNView": {
"buttons": [{
"text": "完成",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "personDetail/editNikeName",
"name": "wxpersonDetaileditNikeName",
"aliasPath": "/wxpersonDetaileditNikeName",
"requireAuth": true,
"style": {
"navigationBarTitleText": "更改名称", //修改备注
"app-plus": {
"titleNView": {
"buttons": [{
"text": "完成",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "personDetail/editchatNo",
"name": "wxpersonDetaileditchatNo",
"aliasPath": "/wxpersonDetaileditchatNo",
"requireAuth": true,
"style": {
"navigationBarTitleText": "更改微聊号", //修改备注
"app-plus": {
"titleNView": {
"buttons": [{
"text": "完成",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "personDetail/editIntro",
"name": "wxpersonDetaileditIntro",
"aliasPath": "/wxpersonDetaileditIntro",
"requireAuth": true,
"style": {
"navigationBarTitleText": "更改个性签名",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "完成",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "personDetail/editGender",
"name": "wxpersonDetaileditGender",
"aliasPath": "/wxpersonDetaileditGender",
"requireAuth": true,
"style": {
"navigationBarTitleText": "更改性别",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "完成",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "personDetail/editAddress",
"name": "wxpersonDetaileditAddress",
"aliasPath": "/wxpersonDetaileditAddress",
"requireAuth": true,
"style": {
"navigationBarTitleText": "更改地区",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "完成",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "personDetail/editPass",
"name": "wxpersonDetaileditPass",
"aliasPath": "/wxpersonDetaileditPass",
"requireAuth": true,
"style": {
"navigationBarTitleText": "设置密码",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "完成",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "personInfo/detail",
"name": "wxpersonInfoDetail",
"aliasPath": "/wxpersonInfoDetail",
"requireAuth": true,
"style": {
"navigationBarTitleText": "信息", //好友个人信息
"app-plus": {
"titleNView": {
"buttons": [{
"text": "\ue623",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "28px"
}]
}
}
}
}, {
"path": "groupInfo/detail",
"name": "wxgroupInfoDetail",
"aliasPath": "/wxgroupInfoDetail",
"requireAuth": true,
"style": {
"navigationBarTitleText": "聊天信息" //群信息
}
}, {
"path": "groupInfo/qunAdd",
"name": "wxqunAdd",
"aliasPath": "/wxqunAdd",
"requireAuth": true,
"style": {
"navigationBarTitleText": "选择联系人"
}
}, {
"path": "groupInfo/qunless",
"name": "wxqunless",
"aliasPath": "/wxqunless",
"requireAuth": true,
"style": {
"navigationBarTitleText": "聊天成员"
}
}, {
"path": "groupInfo/editGroupName",
"name": "wxgroupInfoeditGroupName",
"aliasPath": "/wxgroupInfoeditGroupName",
"requireAuth": true,
"style": {
"navigationBarTitleText": "更改群聊名称", //修改备注
"app-plus": {
"titleNView": {
"buttons": [{
"text": "完成",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "groupInfo/editGroupNotice",
"name": "wxgroupInfoeditGroupNotice",
"aliasPath": "/wxgroupInfoeditGroupNotice",
"requireAuth": true,
"style": {
"navigationBarTitleText": "更改群公告", //修改备注
"app-plus": {
"titleNView": {
"buttons": [{
"text": "修改",
"fontSize": "16px"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "groupInfo/scanCodeDetail",
"name": "wxgroupInfoscanCodeDetail",
"aliasPath": "/wxgroupInfoscanCodeDetail",
"requireAuth": true,
"style": {
"navigationBarTitleText": "群聊信息"
}
}, {
"path": "groupInfo/grouplist",
"name": "wxgroupInfogrouplist",
"aliasPath": "/wxgroupInfogrouplist",
"requireAuth": true,
"style": {
"navigationBarTitleText": "群聊"
}
}, {
"path": "personDetail/QRcode",
"name": "wxpersonDetailQRcode",
"aliasPath": "/wxpersonDetailQRcode",
"requireAuth": true,
"style": {
"navigationBarTitleText": "二维码名片",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "\ue623",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "28px"
}]
}
}
}
}, {
"path": "groupInfo/QRcode",
"name": "wxgroupInfoQRcode",
"aliasPath": "/wxgroupInfoQRcode",
"requireAuth": true,
"style": {
"navigationBarTitleText": "群二维码",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "\ue623",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "28px"
}]
}
}
}
}, {
"path": "personInfo/edit",
"name": "wxpersonInfoedit",
"aliasPath": "/wxpersonInfoedit",
"requireAuth": true,
"style": {
"navigationBarTitleText": "资料设置" //好友资料设置
}
}, {
"path": "friendsCircle/index",
"name": "wxfriendsCircle",
"aliasPath": "/wxfriendsCircle",
"requireAuth": true,
"style": {
"navigationBarTitleText": "朋友圈",
"enablePullDownRefresh" : true,
"app-plus": {
"titleNView": {
"type": "transparent",
"titleColor": "#fff",
"backgroundColor": "#d1d1d1",
"buttons": [{
"text": "\ue638",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "24px",
"background": "rgba(0,0,0,0)"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "friendsCircle/person",
"name": "wxfriendsCirclePerson",
"aliasPath": "/wxfriendsCirclePerson",
"requireAuth": true,
"style": {
"navigationBarTitleText": "我的相册",
"enablePullDownRefresh" : true,
"app-plus": {
"titleNView": {
"type": "transparent",
"titleColor": "#fff",
"backgroundColor": "#d1d1d1",
"buttons": [{
"text": "\ue6f4",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "24px",
"background": "rgba(0,0,0,0)"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "friendsCircle/sendMsg",
"name": "wxsendMsg",
"aliasPath": "/wxsendMsg",
"requireAuth": true,
"style": {
"navigationBarTitleText": "发朋友圈",
"app-plus": {
"titleNView": {
// "type": "transparent",
// "titleColor": "#333333",
// "backgroundColor": "#09C160",
"buttons": [{
"color": "#09C160",
"text": "发表",
// "fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "16px",
"borderRadius": "0px",
"background": "#09C160"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "feedback/index",
"name": "wxfeedback",
"aliasPath": "/wxfeedback",
"requireAuth": true,
"style": {
"navigationBarTitleText": "建议反馈",
"app-plus": {
"titleNView": {
"buttons": [{
"color": "#09C160",
"text": "发表",
"fontSize": "16px",
"borderRadius": "0px",
"background": "#09C160"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}, {
"path": "favorites/index",
"name": "wxfavorites",
"aliasPath": "/wxfavorites",
"requireAuth": true,
"style": {
"navigationBarTitleText": "收藏",
"enablePullDownRefresh" : true,
"app-plus": {
"titleNView": {
// "buttons": [{
// "text": "\ue657",
// "fontSrc": "/static/wx_iconfont.ttf",
// "fontSize": "28px"
// }]
}
}
}
}, {
"path": "friendsCircle/detail",
"name": "wxfriendsCircledetail",
"aliasPath": "/wxfriendsCircledetail",
"requireAuth": true,
"style": {
"navigationBarTitleText": "详情",
"app-plus": {
"titleNView": {
// "type": "transparent",
// "titleColor": "#333333",
// "backgroundColor": "#09C160",
"buttons": [{
"text": "\ue623",
"fontSrc": "/static/wx_iconfont.ttf",
"fontSize": "24px",
"background": "rgba(0,0,0,0)"
}],
"backButton": {
"background": "rgba(0,0,0,0)"
}
}
}
}
}
]
},
{
"root": "wl",
"pages": [{
"path": "login/index",
"name": "wllogin",
"aliasPath": "/wllogin",
"requireAuth": false,
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
}]
}
]
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,61 @@
<template>
<view class="wx-index">
<image class="wx-bg" src="../../static/wx/bg.jpg" mode="aspectFill"></image>
<view class="wx-btns">
<view class="wx-btn wx-btn-info" @click="goLogin">登录</view>
<view class="wx-btn wx-btn-normal" @click="goRegister">注册</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
onLoad() {},
methods: {
goLogin(){
uni.navigateTo({
url:'../../wx/login/index'
})
},
goRegister(){
uni.navigateTo({
url:'../../wx/register/index'
})
}
}
}
</script>
<style scoped>
.wx-index{
width: 100vw;
height: 100vh;
overflow: hidden;
position: relative;
}
.wx-btns{display: flex;flex-direction: row;align-items: center;justify-content: space-between;position: absolute;width: 100%;bottom: 40rpx;padding:0 34rpx;box-sizing: border-box;}
.wx-btn{
width: 200rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 12rpx;
background-color: #007AFF;
color: #fff;
font-size: 32rpx;
}
.wx-btn-info{
background-color: #05C160;
}
.wx-btn-normal{
background-color: #18181A;
}
.wx-bg{
width: 100vw;
height: 100vh;
}
</style>

BIN
im-uniapp/static/call.mp3 Normal file

Binary file not shown.

View File

@@ -0,0 +1,129 @@
@font-face {
font-family: "customicons"; /* Project id 2878519 */
src:url('/static/customicons.ttf') format('truetype');
}
.customicons {
font-family: "customicons" !important;
}
.youxi:before {
content: "\e60e";
}
.wenjian:before {
content: "\e60f";
}
.zhuanfa:before {
content: "\e610";
}
@font-face {
font-family: "wxfont";
src:url('/static/wx_iconfont.ttf') format('truetype');
}
.wxfont{
font-family: "wxfont" !important;
}
.tupian:before {
content: "\e626";
}
.caidan:before {
content: "\e600";
}
.zan:before {
content: "\e6CD";
}
.pinglun:before {
content: "\e662";
}
.yuyin3:before {
content: "\e629";
}
.shipin:before {
content: "\e7dd";
}
.faxiaoxi:before {
content: "\e61c";
}
.xiangji:before {
content: "\e603";
}
.jiahaoyou:before {
content: "\e611";
}
.xiaoxi:before {
content: "\e69e";
}
.saoyisao:before {
content: "\e60a";
}
.nan:before {
content: "\e609";
}
.nv:before {
content: "\e608";
}
.msglist:before {
content: "\e6f4";
}
.gengduo:before {
content: "\e657";
}
.dingwei:before {
content: "\e7e6";
}
.dingwei2:before {
content: "\e675";
}
.mingpian:before {
content: "\e63c";
}
.shoucang:before {
content: "\e646";
}
.fssb:before {
content: "\e61a";
}
.yuyin:before {
content: "\e8c4";
}
.yrecord:before {
content: "\e79d";
}
.yuyin2:before {
content: "\e66c";
}
.jianpan:before {
content: "\e661";
}
.bofang:before {
content: "\e6a6";
}
.xiazai:before {
content: "\e617";
}
.wxcopy:before {
content: "\e75f";
}
.wxdelete:before {
content: "\e63f";
}
.jia:before {
content: "\e620";
}
.jian:before {
content: "\e621";
}
.qunl:before {
content: "\e612";
}
.yspin:before {
content: "\e670";
}
.biaoqing:before {
content: "\e60b";
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Some files were not shown because too many files have changed in this diff Show More