处理page.json冲突

This commit is contained in:
2022-01-20 10:20:05 +08:00
18 changed files with 1529 additions and 265 deletions

View File

@@ -4,7 +4,7 @@
"configurations": [{
"app-plus" :
{
"launchtype" : "local"
"launchtype" : "remote"
},
"default" :
{

View File

@@ -1,13 +1,13 @@
<script>
import {
getVersions
} from './apis/interfaces/versions'
import { getVersions } from './apis/interfaces/versions'
import im from '@/utils/im/index.js'
export default {
onLaunch: function() {
im.initIm('lmxuhwaglu76d')
return
//#ifdef APP-PLUS
// 获取系统版本号
getVersions({

View File

@@ -17,7 +17,7 @@ const config = {
let loginHintState = false
// 网络请求
const request = (parameter, hideLoding) => {
const request = (parameter, hideLoding = true) => {
// 检查url配置
if(parameter.url === 'undefined' || parameter.url === ''){
uni.showToast({

View File

@@ -14,11 +14,10 @@ const getFriends = () => {
}, true)
}
const getUserInfo = async (targetId) => {
const [err, res] = await uni.request({
url: 'http://api.zh.shangkelian.cn/api/im/userInfo/' + targetId,
const getUserInfo = (targetId) => {
return request({
url: 'im/userInfo/' + targetId,
})
return res.data.data
}
export {

12
main.js
View File

@@ -10,6 +10,11 @@ import Vue from 'vue'
import store from './store'
import uView from 'uview-ui'
import filters from './utils/filters.js'
import {
usqlite
} from '@/uni_modules/onemue-USQLite/js_sdk/usqlite.js'
import model from '@/utils/im/models.js'
import {
router,
RouterMount
@@ -24,6 +29,13 @@ Vue.use(router)
Vue.config.productionTip = false
Vue.prototype.$store = store
uni.$sql = usqlite
uni.model = model
uni.$sql.connect({
name: 'zh-health',// 数据库名称
path:'_doc/health.db', // 路径
})
App.mpType = 'app'
const app = new Vue({

View File

@@ -389,8 +389,11 @@
},
{
"path": "pages/im/private/index",
"name": "imPrivate",
"style": {
"navigationBarTitleText": "聊",
"navigationBarTitleText": "聊",
"navigationBarBackgroundColor": "#FFFFFF",
"disableScroll": true,
"app-plus": {
"titleNView": {
"type": "default",
@@ -408,36 +411,42 @@
},
{
"path": "pages/im/private/setting",
"name": "imPrivateSetting",
"style": {
"navigationBarTitleText": "设置"
}
},
{
"path": "pages/im/friends/index",
"name": "imFriends",
"style": {
"navigationBarTitleText": "我的好友"
}
},
{
"path": "pages/im/friends/pending",
"name": "imFriendsPending",
"style": {
"navigationBarTitleText": "新的朋友"
}
},
{
"path": "pages/im/friends/info",
"name": "imFriendsInfo",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/im/friends/mine",
"name": "imFriendsMine",
"style": {
"navigationBarTitleText": "我的资料"
}
},
{
"path": "pages/im/group/index",
"name": "imGroup",
"style": {
"navigationBarTitleText": "我的群聊"
}

View File

@@ -2,12 +2,12 @@
<div>
<view class="list">
<view class="list__item" @click="toPending">
<u-avatar shape="square" size="35" icon="man-add-fill" fontSize="26" randomBgColor></u-avatar>
<u-avatar size="35" icon="plus-people-fill" fontSize="26" randomBgColor></u-avatar>
<text class="list__item__user-name">新的朋友</text>
</view>
<u-line></u-line>
<view class="list__item" @click="toGroup">
<u-avatar shape="square" size="35" icon="chrome-circle-fill" fontSize="26" randomBgColor></u-avatar>
<u-avatar size="35" icon="account-fill" fontSize="26" randomBgColor></u-avatar>
<text class="list__item__user-name">我的群聊</text>
</view>
<u-line></u-line>
@@ -25,6 +25,7 @@
<view class="list" v-for="(item1, index1) in item" :key="index1">
<view class="list__item" @click="toInfo">
<image class="list__item__avatar" :src="item1.url"></image>
<!-- <u-avatar size="35" icon="chrome-circle-fill" fontSize="26" randomBgColor></u-avatar> -->
<text class="list__item__user-name">{{item1.name}}</text>
</view>
<u-line></u-line>
@@ -119,7 +120,7 @@
&__avatar {
height: 35px;
width: 35px;
border-radius: 3px;
border-radius: 50%;
}
&__user-name {

View File

@@ -1,43 +1,76 @@
<template>
<div>
<div v-if="$store.state.token != ''">
<div class="container">
<div class="msg-item u-border-bottom" v-for="(item, index) in conversations" @click="toDetail(item)">
<div class="avatar">
<u-badge numberType="ellipsis" max="99" shape="horn" absolute :offset="[-7, -7]"
:value="item.unreadMessageCount" />
<u-avatar shape="square" :src="friend(item.targetId).portraitUrl"></u-avatar>
</div>
<div class="content ">
<div class="name">
<h3>{{ friend(item.targetId).name }}</h3>
<span class="time">{{ item.sentTime|timeCustomCN }}</span>
</div>
<div class="u-line-1">{{ item.latestMessage.content }}</div>
</div>
</div>
</div>
<u-loadmore status="nomore" />
</div>
<div v-else>
<u-button @click="toLogin">去登录</u-button>
</div>
</div>
<view class="content">
<view v-if="$store.state.token != ''">
<block v-if="conversations.length < 1">
<view class="vertical null-list">
<u-empty
icon="http://cdn.uviewui.com/uview/empty/message.png"
textColor="#999"
text="暂无好友消息"
>
<template>
<view class="null-list-btn">开启聊天</view>
</template>
</u-empty>
</view>
</block>
<block v-else>
<view v-for="(item, index) in conversations" :key="index" class="mssage-box" @click="toDetail(item)">
<view class="mssage-action">
<block v-if="!friend(item.targetId).portraitUrl">
<u-avatar
clsss="mssage-action-cover"
size="44"
:text="friend(item.targetId).name ? friend(item.targetId).name.substring(0,1) : '未'"
font-size="14"
randomBgColor
></u-avatar>
</block>
<block v-else>
<u-avatar
clsss="mssage-action-cover"
:src="friend(item.targetId).portraitUrl"
size="44"
></u-avatar>
</block>
<view class="mssage-action-content">
<view class="mssage-header">
<view class="header-name">{{ friend(item.targetId).name || '未知用户' }}</view>
<view class="header-time">{{ item.sentTime|timeCustomCN }}</view>
</view>
<view class="mssage-msg">{{ item.latestMessage.content }}</view>
</view>
</view>
</view>
</block>
</view>
<!-- 未登录 -->
<view v-else class="vertical null-list">
<u-empty
icon="http://cdn.uviewui.com/uview/empty/permission.png"
textColor="#999"
text="登录后开启聊天吧~"
>
<template>
<view class="null-list-btn" @click="toLogin">去登录</view>
</template>
</u-empty>
</view>
</view>
</template>
<script>
import * as RongIMLib from '@rongcloud/imlib-uni'
import im from '@/utils/im/index.js'
import userAuth from '@/public/userAuth'
import {
getImToken
} from '@/apis/interfaces/im.js'
import { getImToken } from '@/apis/interfaces/im.js'
export default {
data() {
return {
isShown: true, // 当前页面显示状态
conversations: [] // 会话列表
conversations: [] ,// 会话列表
isImToken: '', // 是否已鉴权
}
},
computed: {
@@ -47,31 +80,33 @@
}
}
},
onLoad() {
onShow() {
if (this.$store.state.token !== '') {
if(this.isImToken === ''){
getImToken().then(res => {
im.syncFriends()
im.connect(res.token, res.userInfo)
this.isImToken = res.token
this.getConversationList()
})
},
onShow() {
}
this.getConversationList()
this.isShown = true
}
},
onHide() {
this.isShown = false
},
onNavigationBarButtonTap(e) {
if (e.index == 0) {}
if (e.index == 1) {
uni.navigateTo({
url: '/pages/im/friends/index',
fail: (err) => {
console.log(err);
}
if (e.index == 0) {
uni.showToast({
title: '开发中暂未开放,敬请期待',
icon : 'none'
})
}
if (e.index == 1) {
this.$Router.push({name: 'imFriends'})
}
},
watch: {
'$store.getters.newMessage': function(n, o) {
@@ -105,50 +140,80 @@
// 进入聊天的详情页面,清理未读消息数量
toDetail(item) {
uni.navigateTo({
url: '/pages/im/private/index?targetId=' + item.targetId +
'&conversationType=' + item.conversationType
url: '/pages/im/private/index?targetId=' + item.targetId + '&conversationType=' + item.conversationType
})
}
}
}
</script>
<style scoped lang="scss">
.container {
padding: 0 $uni-spacing-col-lg $uni-spacing-col-lg $uni-spacing-col-lg;
}
.msg-item {
display: flex;
align-items: center;
padding: $uni-spacing-col-lg 0;
.avatar {
position: relative;
margin-right: $uni-spacing-col-lg;
.u-badge {
z-index: 9;
}
}
<style lang="scss" scoped>
.content{
flex: 1;
.name {
background-color: $window-color;
min-height: 100vh;
.null-list{
height: 100vh;
text-align: center;
&-btn{
margin-top: $margin * 2;
line-height: 70rpx;
color: $main-color;
border:solid 1rpx $main-color;
padding: 0 ($padding*3);
font-size: $title-size-m;
border-radius: 35rpx;
box-sizing: border-box;
}
}
.mssage-box{
background: white;
.mssage-action{
position: relative;
padding: 20rpx $padding;
&::after{
position: absolute;
left: $padding + 108;
right: 0;
bottom: 0;
content: " ";
height: 1rpx;
background: $border-color;
}
&-content{
position: absolute;
top: 20rpx;
height: 44px;
left: $padding + 108;
right: $margin;
display: flex;
flex-direction: column;
justify-content: center;
.mssage-header{
display: flex;
font-size: $title-size;
line-height: 40rpx;
justify-content: space-between;
font-size: $uni-font-size-base;
.time {
font-size: $uni-font-size-sm;
color: $uni-text-color-grey;
.header-name{
flex: 1;
@extend .nowrap;
}
.header-time{
padding-left: $padding;
font-size: $title-size-sm - 2;
color: $text-gray;
}
}
.u-line-1 {
color: $u-info;
font-size: $uni-font-size-sm;
.mssage-msg{
font-size: $title-size-sm - 2;
color: $text-gray;
@extend .nowrap;
}
}
}
&-item:last-child{
.mssage-action::after{
display: none;
}
}
}
}

View File

@@ -1,41 +1,33 @@
<template>
<div>
<scroll-view ref="scrollview" :scroll-y="true" class="scroll" :scroll-into-view="scrollIntoID"
:scroll-with-animation="false">
<view v-for="(item,index) in messages" :id="'chatId_'+index">
<div :class="item.messageDirection == 1 ? 'right' : 'left'">
<div class="avatar" v-if="item.messageDirection == 2">
<u-avatar :src="userInfo.portraitUrl" @click="showFriend" shape="square" />
</div>
<div class="msg">
{{ item.content.content }}
<div class="status" v-if="item.messageDirection == 1">
<u-icon v-if="item.sentStatus == 50" name="checkbox-mark" />
<span v-else>未读</span>
</div>
</div>
<div class="avatar" v-if="item.messageDirection == 1">
<u-avatar text="Me" @click="showMine" shape="square" />
</div>
</div>
<div class="time">{{ item.sentTime|timeCustomCN }}</div>
<view class="chat-content">
<scroll-view class="chat-scrool" :scroll-y="true" :scroll-into-view="scrollIntoID" :scroll-with-animation="false">
<!-- 聊天窗口 -->
<view class="chat-item" v-for="(item,index) in messages" :key="index" :id="'chatId_'+index">
<view class="chat-item-time">
<text>{{ item.sentTime|timeCustomCN }}</text>
</view>
<view class="chat-item-article" :class="item.messageDirection == 1 ? 'right' : 'left'">
<view class="chat-msg">
<view class="chat-msg-text">{{ item.content.content }}</view>
<!-- 预留一些图片语音表情包等位置 -->
</view>
<view class="chat-status" :class="{'hide': item.sentStatus == 50}" v-if="item.messageDirection == 1">{{ item.sentStatus == 50 ? '已读': '未读'}}</view>
<view class="chat-avatar">
<!-- <u-avatar :src="userInfo.portraitUrl" @click="showFriend"></u-avatar> -->
<u-avatar
text="无"
fontSize="14"
bg-color="rgba(0,0,0,.2)"
></u-avatar>
</view>
</view>
</view>
</scroll-view>
<div class="footer">
<div class="left">
<u-icon name="volume" />
</div>
<div class="middle">
<u--input autoBlur confirmHold confirmType="send" @confirm="send" v-model="inputTxt" />
</div>
<div class="right">
<u-button type="primary" v-if="showSendButton" class="custom-style" text="发送" @click="send"></u-button>
<u-icon v-else name="plus" />
</div>
</div>
</div>
<view class="chat-footer">
<input class="chat-input" type="text" v-model="inputTxt" confirm-type="发送" @confirm="send" cursor-spacing="10"/>
<button class="chat-push" size="mini" @click="send">发送</button>
</view>
</view>
</template>
<script>
@@ -188,98 +180,117 @@
</script>
<style lang="scss" scoped>
$footer-height: 55px;
.scroll {
height: calc(100vh - 55px);
width: 100vw;
padding: 0 $uni-spacing-col-lg;
.chat-content{
height: 100vh;
background: $window-color;
.chat-scrool{
height: calc(100vh - 140rpx);
box-sizing: border-box;
padding-bottom: $padding;
.chat-item{
.chat-item-time{
text-align: center;
padding: $padding/2 $padding;
text{
background: rgba($color: #000000, $alpha: .2);
color: white;
font-size: $title-size-sm - 4;
line-height: 40rpx;
padding: 0 15rpx;
display: inline-block;
border-radius: $radius-lg;
}
.footer {
width: 100%;
display: flex;
}
.chat-item-article{
position: relative;
padding: 10rpx ($padding + 110) 0;
overflow: hidden;
min-height: 40px;
.chat-msg{
overflow: hidden;
.chat-msg-text{
display: inline-block;
padding: ($padding - 10) $padding;
color: $text-color;
box-sizing: border-box;
font-size: $title-size-lg;
}
}
.chat-status{
color: $text-gray;
font-size: $title-size-sm;
text-align: right;
&.hide{
color: $text-gray-m;
}
}
.chat-avatar{
position: absolute;
top: 0;
}
&.left{
.chat-avatar{
left: $margin;
}
.chat-msg{
.chat-msg-text{
background-color: white;
border-radius: 0 $radius*2 $radius*2 $radius*2;
}
}
}
&.right{
.chat-avatar{
right: $margin;
}
.chat-msg{
text-align: right;
.chat-msg-text{
border-radius: $radius*2 0 $radius*2 $radius*2;
background: $main-color;
color: white;
}
}
}
}
}
}
.chat-footer{
position: fixed;
bottom: 0;
left: 0;
align-items: center;
height: $footer-height;
background-color: $uni-bg-color-grey;
.u-icon {
padding: $uni-spacing-col-sm;
border: 1px solid $u-content-color;
border-radius: $uni-border-radius-circle;
right: 0;
height: 140rpx;
padding: 20rpx ($padding + 150rpx) 40rpx $padding;
background: white;
box-sizing: border-box;
z-index: 99;
.chat-input{
background: $window-color;
height: 80rpx;
border-radius: $radius-lg;
font-size: $title-size-m;
padding: 0 $padding;
box-sizing: border-box;
}
.left {
padding: 0 $uni-spacing-col-lg;
justify-content: center;
}
.middle {
flex: 1;
.u-input {
background-color: $uni-bg-color;
}
}
.right {
padding: 0 $uni-spacing-col-lg;
justify-content: center;
}
}
.custom-style {
height: 36px;
}
.time {
text-align: center;
font-size: $uni-font-size-sm;
color: $u-light-color;
}
.left,
.right {
display: flex;
align-items: top;
.avatar {
margin-top: $uni-spacing-col-base;
}
.msg {
font-size: $uni-font-size-base;
margin: $uni-spacing-col-base;
padding: $uni-spacing-col-base;
word-wrap: break-word;
width: 60%;
border-radius: $uni-border-radius-base;
position: relative;
.status {
.chat-push[size='mini']{
position: absolute;
right: 8rpx;
bottom: 5rpx;
font-size: $uni-font-size-base / 1.5;
color: $uni-text-color-grey;
right: $margin;
top: 20rpx;
height: 80rpx;
line-height: 80rpx;
padding: 0;
margin: 0;
width: 120rpx;
background: $main-color;
color: white;
border-radius: $radius-lg;
font-size: $title-size-m;
font-weight: bold;
&::after{
display: none;
}
}
}
.left {
.msg {
background-color: $uni-bg-color-grey;
}
}
.right {
justify-content: flex-end;
.msg {
background-color: $u-primary-disabled;
}
}
</style>

View File

@@ -65,4 +65,5 @@
</script>
<style>
</style>

View File

@@ -1,11 +1,9 @@
import {
getUserInfo
} from "@/apis/interfaces/im.js"
import im from "@/utils/im/index.js"
export default {
state: {
newMsg: {},
friends: [],
friends: {},
sender: {}
},
getters: {
@@ -16,11 +14,21 @@ export default {
return state.friends
},
userInfo: (state) => (targetId) => {
if (state.friends.filter((item) => item.userId == targetId)[0]) {
return state.friends.filter((item) => String(item.userId) == targetId)[0]
if (state.friends[targetId]) {
return state.friends[targetId]
} else {
return getUserInfo(targetId)
console.log('没找到', targetId);
im.syncUserInfo(targetId)
return {
name: '',
address: '',
hash: '',
portraitUrl: ''
}
}
},
hasUser: (state) => (targetId) => {
return Boolean(state.friends[targetId])
},
sender(state) {
return state.sender
@@ -30,8 +38,8 @@ export default {
newMessage(state, msg) {
Vue.set(state, 'newMsg', msg)
},
updateFriends(state, list) {
state.friends = list
updateFriends(state, userInfo) {
Vue.set(state.friends, userInfo.userId, userInfo)
},
SET_state_sender(state, userInfo) {
state.sender = userInfo
@@ -43,15 +51,77 @@ export default {
}, msg) {
commit('newMessage', msg)
},
updateFriends({
commit
}, list) {
commit('updateFriends', list)
},
setSenderInfo({
commit
}, userInfo) {
commit('SET_state_sender', userInfo)
},
updateFriends({
commit
}, userInfo) {
commit('updateFriends', userInfo)
const model = uni.model.friendModel
model.find('userId=' + userInfo.userId, (err, user) => {
if (!err && user.length == 0) {
console.log('哪里更新的 ', 1);
saveAvatar(userInfo, (savedFilePath) => {
model.insert({
userId: userInfo.userId,
name: userInfo.name,
hash: userInfo.hash,
address: userInfo.address,
portraitUrl: savedFilePath,
}, (err, result) => {})
userInfo.portraitUrl = savedFilePath
commit('updateFriends', userInfo)
})
} else if (!err && user[0].hash != userInfo.hash) {
console.log('哪里更新的 ', 2);
saveAvatar(userInfo, (savedFilePath) => {
model.update('userId=' + userInfo.userId, {
name: userInfo.name,
hash: userInfo.hash,
portraitUrl: savedFilePath,
}, (err, result) => {})
userInfo.portraitUrl = savedFilePath
commit('updateFriends', userInfo)
})
} else if (!err && user[0].portraitUrl.length > 50) {
saveAvatar(userInfo, (savedFilePath) => {
model.update('userId=' + userInfo.userId, {
name: userInfo.name,
hash: userInfo.hash,
portraitUrl: savedFilePath,
}, (err, result) => {})
userInfo.portraitUrl = savedFilePath
commit('updateFriends', userInfo)
})
} else {
console.log('不需要有动作', user[0]);
}
})
}
}
}
const saveAvatar = (userInfo, callback) => {
uni.downloadFile({
url: userInfo.portraitUrl,
success: ({
tempFilePath
}) => {
uni.saveFile({
tempFilePath: tempFilePath,
success: ({
savedFilePath
}) => {
callback(savedFilePath)
}
})
},
fail: (err) => {
console.log('头像保存失败', err);
}
})
}

View File

@@ -0,0 +1,28 @@
## 2.1.02022-01-14
1. 新增了添加多个数据的事件选项options, index感谢`@风扬`
## 2.1.22022-01-14
1. 新增了添加多个数据的事件选项options, index感谢`@风扬`
## 2.1.02022-01-14
1. 新增了添加多个数据的事件选项options, index感谢`@风扬`
## 2.0.22022-01-05
1. 新增了联合主键,详见文档
## 2.0.12021-12-30
1. 关闭了sql 输出信息
## 2.0.02021-12-30
1. 重构了代码
2. 优化了调用模式
3. 提供了相对完善示例项目
## 1.2.02021-12-22
1. 移除了不设置主键自动增加主键,已经有的无需处理,原有组件自增 无需操作
2. 增加了表的重命名
3. 增加了列表的新建列
## 1.1.02021-12-20
1. 增加了约束相关内容
## 1.0.12021-12-17
1. 修复了特殊表名无法使用的问题例如`[object Object]`
## 1.0.02021-12-16
基于 html5-Plus的 sqlite 数据库方法封装
1. 封装了简单的增删改查方法
2. 使用ORM方式操作数据
3. 可以使用链式调用
4. 支持原生语法

View File

@@ -0,0 +1,612 @@
/**
* 对 SQLite 的 ORM 的封装处理
* @time 2021-12-30 11:00:00
* @version 2.0.0
*
* @by onemue
*/
let config = {
deBug: true,
isConnect: false
}
class Response {
constructor(code, msg, data) {
this.code = code;
this.msg = msg;
this.data = data;
}
toString() {
return JSON.stringify(this);
}
}
class Utils {
static modelSql(name, options) {
let sql;
let sqlArr = [];
let primaryKeyArr = [];
Utils.log('options:' + options);
for (const key in options) {
if (Object.hasOwnProperty.call(options, key)) {
const option = options[key];
sqlArr.push(Utils.restrain(key, option));
if (option.primaryKey == true) {
primaryKeyArr.push(key.toString());
Utils.log(`${key} is primary key${primaryKeyArr.length}`);
}
}
}
Utils.log(primaryKeyArr.length);
if (primaryKeyArr.length >= 1) {
sql = `CREATE TABLE '${name}' (${sqlArr.join(', ')}, PRIMARY KEY (${primaryKeyArr.join()}))`;
} else {
sql = `CREATE TABLE '${name}' (${sqlArr.join(', ')})`;
}
Utils.log(`modelSql :${sql}`);
return sql;
}
static restrain(key, options) {
let restrainArray = [];
restrainArray.push(`'${key}'`);
// 如果是 String 拦截处理
if (options.constructor != Object) {
restrainArray.push(Utils.toType(options));
return restrainArray.join(' ');
}
restrainArray.push(Utils.toType(options.type));
// 非空
if (options.notNull == true) {
restrainArray.push('NOT NULL');
}
// 默认值
if (options.default) {
restrainArray.push(`DEFAULT ${options.default}`);
}
// 是否是不同的值
if (options.unique == true) {
restrainArray.push('UNIQUE');
}
// 主键
// if (options.primaryKey === true) {
// restrainArray.push('PRIMARY KEY');
// }
// 检查
if (options.check) {
restrainArray.push(`CHECK(${THIS_VALUE.check})`);
}
return restrainArray.join(' ');
}
// 联合主键
static getUnionPrimaryKey() {
}
static toType(jsType) {
let sqliteType = '';
if (jsType == Number) {
sqliteType = 'numeric';
} else if (jsType == Date) {
sqliteType = 'timestamp';
} else {
sqliteType = 'varchar';
}
return sqliteType;
}
static log() {
if (config.deBug) {
console.log.apply(null, arguments);
}
}
}
/**
* Model 对象内部public方法全部 return this;
*/
class Model {
/**
* @constructor
* @param {String} name 数据库表名
* @param {Object} options 数据表列对象
* @returns
*/
constructor(name, options) {
let self = this;
self.name = name;
self.options = options;
if (config.isConnect) {
self.repair();
} else {
console.error('no connect');
}
}
/**
* @description 查询表数据
* @param {String|Array} options
* - String WHERE 内容
* - Array 需要查询的列
* @param {*} callback
* @returns
*/
find(options, callback) {
let sql = '';
let self = this;
self.repair();
if (!callback) {
sql = `SELECT * FROM '${this.name}'`; // 查找全部
callback = options;
} else if (options.constructor == Array) {
sql = `SELECT ${options.join()} FROM '${this.name}'`; // 查找制定列
} else if (options.constructor == String) {
sql = `SELECT * FROM '${this.name}' WHERE ${options}`; // 制定条件查询
};
Utils.log(`find: ${sql}`);
plus.sqlite.selectSql({
name: config.name,
sql: sql,
success(e) {
callback(null, e);
},
fail(e) {
callback(e);
}
});
return self;
}
/**
* @description 分页查询
* @param {Object} options : { where:查询条件, number: 当前页数 , count : 每页数量 }
* @param {Function} callback :err,results=>{}
* @return
*/
limit(options, callback) {
let sql = '';
let self = this;
self.repair();
if (!options.where) {
// 不存在 where
sql =
`SELECT * FROM '${this.name}' LIMIT ${options.count} OFFSET ${(options.number - 1) * options.count}`
} else {
// 存在 where
sql =
`SELECT * FROM '${this.name}' WHERE ${options.where} LIMIT ${options.count} OFFSET ${(options.number - 1) * options.count}`;
};
Utils.log(`limit: ${sql}`);
plus.sqlite.selectSql({
name: config.name,
sql: sql,
success(e) {
callback(null, e);
},
fail(e) {
callback(e);
}
});
return this;
}
/**
* @description 插入数据
* @param {Object|Array} options: 需要插入的单个或者多个数据
* @param {Function} callback :err,results=>{}
*/
insert(options, callback) {
let self = this;
self.repair();
if (config.isConnect) {
if (options.constructor == Array) {
for (var i = 0; i < options.length; i++) {
this.insert(options[i], callback, i);
}
} else if (options.constructor == Object) {
let keys = [];
let values = [];
let index = arguments[3] ?? null;
for (var key in options) {
keys.push(key);
values.push(`'${options[key]}'`);
}
let sql = `INSERT INTO '${this.name}' (${keys.join()}) VALUES (${values.join()})`;
Utils.log(`insert: ${sql}`);
plus.sqlite.executeSql({
name: config.name,
sql: sql,
success(e) {
if (index) {
callback(null, e, options, index);
}
callback(null, e, options);
},
fail(e) {
if (index) {
callback(e, null, options, index);
}
callback(e, null, options);
}
})
}
}
return this;
}
/**
* @description 更新数据
* @param {Object} options可选参数 更新条件
* @param {Object} obj 修改后的数据
* @param {Function} callback :err,results=>{}
*/
update(options, obj, callback) {
let sql = '';
let self = this;
let items = [];
self.repair();
if (!callback) {
// 不存在options
callback = obj;
obj = options;
for (var key in obj) {
items.push(`${key}='${obj[key]}'`);
};
sql = `UPDATE '${this.name}' SET ${items.join()}`;
} else {
// 存在options
for (var key in obj) {
items.push(`${key}='${obj[key]}'`);
};
sql = `UPDATE ${this.name} SET ${items.join()} WHERE ${options}`;
};
Utils.log(`update: ${sql}`);
plus.sqlite.executeSql({
name: config.name,
sql: sql,
success(e) {
callback(null, e);
},
fail(e) {
callback(e);
}
});
return this;
}
/**
* @description 删除数据
* @param {Object} options :可选参数 删除条件
* @param {Function} callback :err,results=>{}
*/
delete(options, callback) {
var sql = '';
let self = this;
self.repair();
if (!callback) {
sql = `DELETE FROM '${this.name}'`;
callback = options;
} else {
sql = `DELETE FROM '${this.name}' WHERE ${options}`;
};
Utils.log(`delete: ${sql}`);
plus.sqlite.executeSql({
name: config.name,
sql: sql,
success(e) {
callback(null, e);
},
fail(e) {
callback(e);
}
});
return this;
}
/**
* @description 重命名或者新增列
* @param {Object} options 参数 数组为新增多列 对象为新增单列{aa} 字符串重命名
* @param {Function} callback :err,results=>{}
* @return:
*/
alter(options, callback) {
let self = this;
let sql = '';
self.repair();
if (options.constructor == Array) { // 新增多列
for (let i = 0; i < options.length; i++) {
self.alter(options[i], callback);
}
} else if (options.constructor == Object) { // 新增单列
let column = Utils.restrain(options.name, options.option);
sql = `ALTER TABLE '${this.name}' ADD COLUMN ${column}`
} else if (options.constructor == String) { // 重命名
sql = `ALTER TABLE '${self.name}' RENAME TO '${options}'`
}
Utils.log(`alter: ${sql}`);
plus.sqlite.selectSql({
name: config.name,
sql: sql,
success(e) {
if (options.constructor == String) { // 重命名
self.name = options;
}
callback(null, e);
},
fail(e) {
callback(e);
}
});
return this;
}
/**
* @description
* @param {Model} model 右 Model
* @param {Object} options
* @param {Function} callback
* @returns
*/
join(model, options, callback) {
if (!model) {
console.error('"model" cannot be empty.');
}
if (options.constructor != Object) {
console.error('The type of "options" is wrong, it should be "Object".');
}
if (!options.type || !options.predicate) {
console.error('Missing required parameters');
}
let leftName = this.name;
let rightName = model.name;
let leftValue = options.predicate.left;
let rightValue = options.predicate.right;
let cols = ['*'];
self.repair();
const SQL_MAP = {
cross: `SELECT ${cols.join()} FROM ${leftName} CROSS JOIN ${rightName};`,
inner: [`SELECT ${cols.join()} FROM ${leftName} NATURAL JOIN ${rightName}`,
`SELECT ${cols.join()} FROM ${leftName} INNER JOIN ${rightName} ON ${leftName}.${leftValue} = ${rightName}.${rightValue}`
],
outer: `SELECT ${cols.join()} FROM ${leftName} OUTER JOIN ${rightName} ON ${leftName}.${leftValue} = ${rightName}.${rightValue}`
}
let sql = '';
if (options.type == inner && !options.predicate) {
sql = SQL_MAP[options.type][0];
} else if (options.type == inner && !options.predicate) {
sql = SQL_MAP[options.type][1];
} else {
sql = SQL_MAP[options.type];
}
Utils.log(`join: ${sql}`);
plus.sqlite.selectSql({
name: config.name,
sql: sql,
success(e) {
callback(null, e);
},
fail(e) {
callback(e);
}
});
return this;
}
/**
* @description 执行sql语句
* @param {String} sql : sql语句
* @param {Function} callback :err,results=>{}
*/
sql(sql, callback) {
let self = this;
self.repair();
Utils.log(`sql: ${sql}`);
plus.sqlite.selectSql({
name: config.name,
sql: sql,
success(e) {
callback(null, e);
},
fail(e) {
callback(e);
}
});
return this;
}
/**
* @description 判断是否存在
* @param {Function} callback
*/
isExist(callback) {
let sql = `SELECT count(*) AS isExist FROM sqlite_master WHERE type='table' AND name='${this.name}'`;
let self = this;
Utils.log(`isExist: ${sql}`);
Utils.log(`isExist: ${config.name}`);
plus.sqlite.selectSql({
name: config.name,
sql: sql,
success(e) {
callback(null, e);
},
fail(e) {
callback(e);
}
});
return this;
}
/**
* @description 删除数据表 **不推荐**
* @param {Function} callback
*/
drop(callback) {
var sql = `DROP TABLE '${this.name}'`;
let self = this;
self.repair();
Utils.log(`drop: ${sql}`);
plus.sqlite.selectSql({
name: config.name,
sql: sql,
success(e) {
callback(null, e);
},
fail(e) {
callback(e);
}
});
return this;
}
/**
* @description 创建数据表 **不推荐**
* @param {Function} callback
*/
create(callback) {
let self = this;
let sql = Utils.modelSql(self.name, self.options);
Utils.log(`create: ${sql}`);
plus.sqlite.executeSql({
name: config.name,
sql: sql,
success(e) {
callback(null, e);
},
fail(e) {
callback(e)
}
});
return this;
}
toString() {
return `[${this.name} Model]`;
}
repair() {
let self = this;
self.isExist(function (e, r) {
if (e) {
console.error(e);
}
if (!r[0].isExist) {
self.create(function (e, r) {
Utils.log(e, r);
});
}
});
}
// TODO 更新表结构
// TODO 数据表备份??
// TODO 多表联查
// TODO 下班了其他的想不起来 回头再说
}
// 单例模式
export class usqlite {
/**
* 构造函数
* @param {Object} options 数据库配置信息 *
* {name: 'demo', path: '_doc/demo.db'}
* - name 数据库名称*
* - path 数据库路径
* @param {Function} callback
*/
constructor(options, callback) {
console.warn('No instantiation');
}
/**
* @description 链接数据库
* @param {Object} options 数据库配置信息 *
* {name: 'demo', path: '_doc/demo.db'}
* - name 数据库名称*
* - path 数据库路径
* @param {Function} callback
*/
static connect(options, callback) {
config.name = options.name; // 数据库名称*
config.path = options.path; // 数据库名称*
plus.sqlite.openDatabase({
name: config.name, //数据库名称
path: config.path, //数据库地址
success(e) {
config.isConnect = true;
callback(null, e);
},
fail(e) {
if (e.code == -1402) {
config.isConnect = true;
}
callback(e);
}
});
}
/**
* @description 断开数据库
* @param {*} callback
*/
static close(callback) {
plus.sqlite.closeDatabase({
name: config.name, //数据库名称
path: config.path, //数据库地址
success(e) {
config.isConnect = false;
callback(null, e);
},
fail(e) {
callback(e);
}
});
}
/**
* @description 创建 Model 对象
* @example
* usqlite.model('demo',
* {
* id: {
* type:Number
* },
* content: String
* })
* @param {String} name 数据表名称 *
* @param {String} options 参数配置 *
* @returns 返回 Model 对象
*/
static model(name, options) {
Utils.log(config);
return new Model(name, options);
}
}

View File

@@ -0,0 +1,80 @@
{
"id": "onemue-USQLite",
"displayName": "u-SQLite V2",
"version": "2.1.0",
"description": "基于ORM技术使用对象的方式来操作sqlite数据库而无需编写任何sql语句.",
"keywords": [
"USQLite,数据库操作,缓存,大数据缓存,sqlite,sql,orm"
],
"repository": "https://github.com/onemue/u-sqlite",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"JS SDK",
"通用 SDK"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "sqlite"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "u"
},
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "n",
"Android Browser": "n",
"微信浏览器(Android)": "n",
"QQ浏览器(Android)": "n"
},
"H5-pc": {
"Chrome": "n",
"IE": "n",
"Edge": "n",
"Firefox": "n",
"Safari": "n"
},
"小程序": {
"微信": "n",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}

View File

@@ -0,0 +1,287 @@
<h2>onemue-USQLite</h2>
> u-SQLite 交流群643867519
> 如果觉的当前插件对你有帮助可以前往 [github](https://github.com/onemue/u-sqlite) 点 star!
### 技术亮点
1. 基于ORM技术使用对象的方式来操作sqlite数据库而无需编写任何sql语句
2. 支持数据库的常规操作增删改查API极为简洁且高效智能。
3. 支持条件查询 与 分页查询
4. 支持原生sql语句如果我们的框架无法满足您的需求我们也提供了特殊的API可以直接使用sql语句来操作
5. 支持链式语法
### 使用方法
#### 挂载方法
```js
// main.js
import { usqlite } from '@/uni_modules/onemue-USQLite/js_sdk/usqlite.js'
uni.$sql = usqlite;
```
#### 链接数据库
```js
//如果数据库存在则连接,不存在则会自动创建数据库
uni.$sql.connect({
name: 'local',// 数据库名称
path:'_doc/local.db', // 路径
});
```
#### 创建Model模型
```js
//3.创建Model(表格模型:负责增删改查)
//如果table表格存在则连接不存在则自动创建
let studentModel = uni.$sql.model('student',{
name:String,
age:{
type: Number,
default: 18
}
});
```
**约束**
> 使用对象时,以约束模式处理,此时对象`type`为类型。
> 使用一下内容,为约束。
> `default`需要设置默认值,如:`{default: 0}`。
> `check`需要设置条件,如:`{check:'age>18'}`。
- check CHECK 检查
- primaryKey PRIMARY KEY 主键
- unique UNIQUE 不重复
- default DEFAULT 默认值
- notNull NOT NULL 非空
**联合主键**
> 在两列的约束同时设置`primaryKey`值为`true`时,可以创建联合主键。
> ~~如果没有主键,会自动添加主键`usql_id`。~~(已经移除)
#### 调用 API 执行操作
```js
//4.调用API添加数据
studentModel.insert({name:'张三10',age:30},(err,results)=>{
console.log(err);
console.log(results);
if(!err) console.log('增加成功');
});
```
### API 文档
#### 1.1 增加操作
```js
studentModel.insert({name:'张三10',age:30},(err,results)=>{
console.log(err);
console.log(results);
if(!err) console.log('增加成功');
});
```
**批量增加**
```js
var arr = [];
for (var i = 1; i <= 10; i++) {
arr.push({ name: '张三', age: 30 })
}
studentModel.insert(arr, (err, results) => {
console.log(err);
console.log(results);
if (!err) console.log('增加成功');
});
```
> v2.1.0
> 感谢 `@风扬`
> 新增了事件选项options, index因为批量增加是遍历数组依次执行所以callback回执行多次可以使用第三第四个options, index参数判断是哪一个添加的数组出现了问题
> 例如:
> ```js
> studentModel.insert(arr, (err, results, option, index) => {
> console.log(err);
> console.log(results);
> if (!err) console.log('增加成功');
> console.log(`第${index}条数据出现问题,姓名是:${option.name}`) // index 从0开始
> });
#### 1.2-查询操作
**1-查询所有数据**
```js
//2.1 查询所有数据
studentModel.find((err,results)=>{
console.log(results);
});
```
**2-查询数据库指定字段数据**
```js
//2.2 根据数据库字段查询部分数据
// ['name'] : 将要查询的字段放入数组中
studentModel.find(['name'],(err,results)=>{
console.log(results);
});
```
**3-条件查询**
```js
//2.3 根据条件查询数据
// 'id=1' : 查询id为1的数据 (查询条件可以参考sql语句)
//例如 'age>10' : 查询age超过10的数据
//例如 'name>"张三"' : 查询名字为张三的数据,注意字符串添加引号
studentModel.find('id>21',(err,results)=>{
console.log(results);
});
```
**4-分页查询**
```js
//2.4 分页查询
// 第一个参数options对象有三个属性 {where:分页查询条件(可选), number:页数 count每页数量}
studentModel.limit({where:'age>28',number:1,count:10},(err,results)=>{
console.log(results);
});
```
#### 1.3-多表查询
> Join 用于结合两个或多个数据库中表的记录。
> 有三种链接形式:
> 交叉连接 - CROSS JOIN 连接的两个表所有数据行的笛卡尔积
> 内连接 - INNER JOIN 只返回 左表在右表中有有匹配的行。
> 外连接 - OUTER JOIN 返回左表所有的行,即使在右表中没有匹配的行
```js
studentModel.join( // 左表
resultModel, // 右表
{
where: 'grade<60', // 筛选条件
type: 'inner', // 链接模式
predicate: { // 谓语匹配词语
left: 'id', // 左表
right: 'id' // 右表
}
},
function(e, r){
})
```
#### 1.4-修改操作
**1-修改所有数据**
```js
//3.1 将数据库中所有的name字段值修改为李四
studentModel.update({name:'李四'},(err,results)=>{
console.log(results);
});
```
**2-条件修改**
```js
//3.2 将数据库中 id = 1 的数据age修改为30
studentModel.update('id=1',{age:30},(err,results)=>{
console.log(results);
});
//3.3 将数据库中所有 age < 20 的数据name修改为王五
studentModel.update('age<20',{name:'王五'},(err,results)=>{
console.log(results);
});
```
#### 1.5-删除操作
```js
//4.1 删除所有 age>30 的数据
studentModel.delete('age>20',(err,results)=>{
console.log(results);
});
//4.2 清空表中所有数据
studentModel.delete((err,results)=>{
console.log(results);
});
```
#### 1.6-执行自定义SQL语句
```js
studentModel.sql('insert into student(name,age) values("andy",20)',(err,results)=>{
console.log(results);
});
```
#### 1.7-删除表格(慎用)
```js
studentModel.drop((err,results)=>{
console.log(results);
});
```
#### 1.8-创建表格(慎用)
```js
studentModel.create((err,results)=>{
console.log(results);
});
```
#### 1.9-数据库是否存在Model对应的数据表(慎用)
```js
studentModel.isExist((err,results)=>{
console.log(results);
});
```
#### 1.10-链式语法支持
```js
studentModel.insert({name:'张三22',age:22},(err,results)=>{
console.log(err);
console.log(results);
})
.find('name="张三22"',(err,results)=>{
console.log(err);
console.log(results);
});
```
#### 1.11-表的相关操作 (慎用)
```js
// 8.1 重命名
studentModel.alter('student_bak',(err,results)=>{
console.log(err);
console.log(results);
})
// 8.2 新增单列
studentModel.alter({name:'id', option:Number},(err,results)=>{
console.log(err);
console.log(results);
})
// 8.3 新增多列
studentModel.alter([
{name:'id', option:Number}, // option 可以使用约束模式
{name:'xx', option:Number},
{name:'xxx', option:Number}
],(err,results)=>{
console.log(err);
console.log(results);
})
```
### JS类型转存SQLite时如何处理
> 参考:
> [sqliet数据类型](https://www.runoob.com/sqlite/sqlite-data-types.html)
总结来说就是大部分都转化成了TEXT格式进行存储。
```js
if (option[key] == Number) {
str += `'${key}' numeric,`;
} else if (option[key] == Date) {
str += `'${key}' timestamp,`;
} else {
str += `'${key}' varchar,`;
}
```
**Number**
usqlite 会将`Number`数据类型转换`numeric`,但是sqlite会根据数据是否可逆转化为`INTEGER`(带符号的整数)或者`REAL`(8 字节的 IEEE 浮点数).
**其他内容**
usqlite 会将其他数据类型转化`varchar`sqlite会将其转化为`TEXT`
### Q&A
1. db 文件存放在什么位置?
> 答: 如果是`_doc`的话存放存放位置参考[H5+ Api - PRIVATE_DOC](https://www.html5plus.org/doc/zh_cn/io.html#plus.io.PRIVATE_DOC)。
> 如果是其他路径,请参考[H5+ Api - SQLite](https://www.html5plus.org/doc/zh_cn/sqlite.html#openDatabase)。
2. 后续更新其他api会不会不兼容当然版本
> 答: 在后续版本中,始终会兼容当前版本。
3. 为什么兼容性这么差只兼容APP版本吗后续会不会兼容其他版本
> 答: 由于**H5+ plus API**限制当前版本只支持APP后续版本会兼容其他内容但是兼容其他版本就不能使用Sqlite而是使用其他`Storage`或者其他内容进行兼容处理部分api将不支持特别是自定sql的api。
> 并且这个这些功能会在后期在开发可能要到年后或者什么时候我近期开发Sqlite的App可视化插件。

View File

@@ -1,7 +1,8 @@
import * as RongIMLib from '@rongcloud/imlib-uni'
import store from '@/store/index.js'
import {
getFriends
getFriends,
getUserInfo
} from '@/apis/interfaces/im.js'
const initIm = (KEY) => {
@@ -18,12 +19,12 @@ const setNotifyBadge = (count) => {
if (code === 0) {
if (count > 0) {
uni.setTabBarBadge({
index: 2,
index: 3,
text: String(count > 99 ? '99+' : count)
})
} else {
uni.removeTabBarBadge({
index: 2
index: 3
})
}
}
@@ -43,6 +44,14 @@ const connect = (token, userInfo) => {
store.dispatch('setSenderInfo', userInfo)
setNotifyBadge()
const model = uni.model.friendModel
model.find((err, results) => {
results.map(item => {
store.dispatch('updateFriends', item)
})
})
}
const disconnect = () => {
@@ -76,9 +85,19 @@ const newMessage = (msg) => {
setNotifyBadge()
if (!store.getters.hasUser(msg.targetId)) {
syncUserInfo(msg.targetId)
}
store.dispatch('newMessage', msg)
}
function syncUserInfo(targetId) {
getUserInfo(targetId).then(res => {
store.dispatch('updateFriends', res)
})
}
// 播放状态
let tipState = false
@@ -133,7 +152,10 @@ const sendMsg = (conversationType, targetId, content, callback) => {
*/
const syncFriends = () => {
getFriends().then(res => {
store.dispatch('updateFriends', res)
res.map(item => {
console.log('item', item);
store.dispatch('updateFriends', item)
})
})
}
@@ -142,5 +164,6 @@ export default {
connect,
sendMsg,
setNotifyBadge,
syncFriends
syncFriends,
syncUserInfo
}

22
utils/im/models.js Normal file
View File

@@ -0,0 +1,22 @@
import {
usqlite
} from '@/uni_modules/onemue-USQLite/js_sdk/usqlite.js'
const friendModel = usqlite.model('friends', {
userId: {
type: String,
primaryKey: true,
unique: true
},
name: String,
address: String,
hash: {
type: String,
unique: true
},
portraitUrl: String
})
export default {
friendModel
}

44
utils/im/sqlite.js Normal file
View File

@@ -0,0 +1,44 @@
const openSqlite = (callback) => {
plus.sqlite.openDatabase({
name: 'friends',
path: '_doc/friends.db',
success: (res) => {
callback(res)
},
fail: (err) => {
callback(err)
}
})
}
const executeSQL = (callback) => {
plus.sqlite.selectSql({
name: 'friends',
sql: sql,
success: (res) => {
callback(res)
},
fail: (err) => {
callback(err)
}
})
}
const closeSqlite = (callback) => {
plus.sqlite.closeDatabase({
name: 'friends',
path: '_doc/friends.db',
success: (res) => {
callback(res)
},
fail: (err) => {
callback(err)
}
})
}
export {
openSqlite,
executeSQL,
closeSqlite
}