websocket

This commit is contained in:
kuaifan 2021-06-04 12:18:28 +08:00
parent 1e6317c597
commit ea5478c2fb
9 changed files with 469 additions and 701 deletions

View File

@ -0,0 +1,172 @@
<?php
namespace App\Services;
@error_reporting(E_ALL & ~E_NOTICE);
use App\Models\User;
use App\Models\WebSocket;
use App\Module\Base;
use App\Tasks\PushTask;
use Cache;
use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
/**
* @see https://wiki.swoole.com/#/start/start_ws_server
*/
class WebSocketService implements WebSocketHandlerInterface
{
/**
* 声明没有参数的构造函数
* WebSocketService constructor.
*/
public function __construct()
{
}
/**
* 连接建立时触发
* @param Server $server
* @param Request $request
*/
public function onOpen(Server $server, Request $request)
{
global $_A;
$_A = [
'__static_langdata' => [],
];
$fd = $request->fd;
$data = Base::newTrim($request->get);
$action = $data['action'];
switch ($action) {
/**
* 网页访问
*/
case 'web':
{
// 判断token参数
$token = $data['token'];
$cacheKey = "ws::token:" . md5($token);
$userid = Cache::remember($cacheKey, now()->addSeconds(1), function () use ($token) {
$authInfo = User::authFind('all', $token);
if ($authInfo['userid'] > 0) {
if (User::whereUserid($authInfo['userid'])->whereEmail($authInfo['email'])->whereEncrypt($authInfo['encrypt'])->exists()) {
return $authInfo['userid'];
}
}
return 0;
});
if (empty($userid)) {
Cache::forget($cacheKey);
$server->push($fd, Base::array2json([
'type' => 'error',
'data' => [
'error' => '会员不存在!'
],
]));
$server->close($fd);
$this->deleteUser($fd);
return;
}
// 保存用户、发送open事件
$this->saveUser($fd, $userid);
$server->push($fd, Base::array2json([
'type' => 'open',
'data' => [
'fd' => $fd,
],
]));
// 重试发送失败的消息
PushTask::resendTmpMsgForUserid($userid);
}
break;
default:
break;
}
}
/**
* 收到消息时触发
* @param Server $server
* @param Frame $frame
*/
public function onMessage(Server $server, Frame $frame)
{
global $_A;
$_A = [
'__static_langdata' => [],
];
//
$msg = Base::json2array($frame->data);
$type = $msg['type']; // 消息类型
$to = $msg['to']; // 发给谁
$msgId = $msg['msgId']; // 消息ID用于回调
$data = $msg['data']; // 消息详情
//
$reData = [];
switch ($type) {
/**
* 收到回执
*/
case 'receipt':
return;
}
//
if ($msgId) {
PushTask::push([
'fd' => $frame->fd,
'msg' => [
'type' => 'receipt',
'msgId' => $msgId,
'data' => $reData,
]
]);
}
}
/**
* 关闭连接时触发
* @param Server $server
* @param $fd
* @param $reactorId
* @throws \Exception
*/
public function onClose(Server $server, $fd, $reactorId)
{
$this->deleteUser($fd);
}
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* 保存用户
* @param $fd
* @param $userid
*/
private function saveUser($fd, $userid)
{
WebSocket::updateInsert([
'key' => md5($fd . '@' . $userid)
], [
'fd' => $fd,
'userid' => $userid,
]);
}
/**
* 清除用户
* @param $fd
*/
private function deleteUser($fd)
{
WebSocket::whereFd($fd)->delete();
}
}

View File

@ -52,14 +52,14 @@ class IhttpTask extends AbstractTask
{
$res = Ihttp::ihttp_request($this->url, $this->post, $this->extra);
if ($this->apiWebsocket && $this->apiUserid) {
$body = Base::isSuccess($res) ? Base::json2array($res['data']) : $res;
$data = Base::isSuccess($res) ? Base::json2array($res['data']) : $res;
PushTask::push([
'userid' => $this->apiUserid,
'msg' => [
'messageType' => 'apiWebsocket',
'type' => 'apiWebsocket',
'apiWebsocket' => $this->apiWebsocket,
'apiSuccess' => Base::isSuccess($res),
'body' => $body,
'data' => $data,
]
]);
}

View File

@ -112,8 +112,8 @@ class PushTask extends AbstractTask
if (!is_array($msg)) {
continue;
}
$messageType = $msg['messageType'];
if (empty($messageType)) {
$type = $msg['type'];
if (empty($type)) {
continue;
}
// 发送对象
@ -149,7 +149,7 @@ class PushTask extends AbstractTask
$userFail[] = WebSocket::whereFd($fid)->value('userid');
}
} else {
$key = "PUSH::" . $fid . ":" . $messageType . ":" . $key;
$key = "PUSH::" . $fid . ":" . $type . ":" . $key;
Cache::put($key, [
'fd' => $fid,
'msg' => $msg,

View File

@ -38,7 +38,7 @@ const router = new VueRouter({
routes
});
//进度条配置
// 进度条配置
ViewUI.LoadingBar.config({
color: '#3fcc25',
failedColor: '#ff0000'
@ -51,28 +51,27 @@ router.afterEach((to, from, next) => {
ViewUI.LoadingBar.finish();
});
//加载函数
// 加载函数
Vue.prototype.goForward = function(location, isReplace) {
if (typeof location === 'string') location = {name: location};
if (isReplace === true) {
this.$router.replace(location);
app.$router.replace(location).then(() => {});
}else{
this.$router.push(location);
app.$router.push(location).then(() => {});
}
};
//返回函数
// 返回函数
Vue.prototype.goBack = function (number) {
let history = $A.jsonParse(window.sessionStorage['__history__'] || '{}');
if ($A.runNum(history['::count']) > 2) {
this.$router.go(typeof number === 'number' ? number : -1);
app.$router.go(typeof number === 'number' ? number : -1);
} else {
this.$router.replace(typeof number === "object" ? number : {path: '/'});
app.$router.replace(typeof number === "object" ? number : {path: '/'}).then(() => {});
}
};
Vue.prototype.$A = $A;
Vue.config.productionTip = false;
const app = new Vue({
@ -83,4 +82,12 @@ const app = new Vue({
components: { App }
});
$A.app = app;
$A.goForward = app.goForward;
$A.goBack = app.goBack;
$A.getLanguage = app.getLanguage;
$A.Message = app.$Message;
$A.Notice = app.$Notice;
$A.Modal = app.$Modal;
$A.store = app.$store;
$A.L = app.$L;

View File

@ -1264,65 +1264,14 @@
if (typeof params.method === 'undefined') params.method = 'GET';
if (typeof params.timeout === 'undefined') params.timeout = 30000;
if (typeof params.dataType === 'undefined') params.dataType = 'json';
if (typeof params.beforeSend === 'undefined') params.beforeSend = () => { };
if (typeof params.before === 'undefined') params.before = () => { };
if (typeof params.complete === 'undefined') params.complete = () => { };
if (typeof params.afterComplete === 'undefined') params.afterComplete = () => { };
if (typeof params.after === 'undefined') params.after = () => { };
if (typeof params.success === 'undefined') params.success = () => { };
if (typeof params.error === 'undefined') params.error = () => { };
if (typeof params.header == 'undefined') params.header = {};
//
let loadText = "数据加载中.....";
let busyNetwork = "网络繁忙,请稍后再试!";
if (typeof $A.app === 'object' && typeof $A.app.$L === 'function') {
loadText = $A.app.$L(loadText);
busyNetwork = $A.app.$L(busyNetwork);
}
//
let toastID = null, beforeTitle = '', errorTitle = '';
if (typeof $A.app === 'object' && typeof $A.app.$Message === 'object') {
if (typeof params.beforeSend === 'string') {
beforeTitle = params.beforeSend;
params.beforeSend = () => { toastID = $A.app.$Message.loading({content:beforeTitle, duration: 0}); };
}else if (params.beforeSend === true) {
params.beforeSend = () => { toastID = $A.app.$Message.loading({content:loadText, duration: 0}); };
}
if (typeof params.error === 'string') {
errorTitle = params.error;
params.error = () => { $A.app.$Message.error({content:errorTitle, duration: 5}); };
}else if (params.error === true) {
params.error = () => { $A.app.$Message.error({content:busyNetwork, duration: 5}); };
}
if (params.complete === true) {
params.complete = () => { toastID?toastID():'' };
}
}else{
if (typeof params.beforeSend === 'string') {
beforeTitle = params.beforeSend;
params.beforeSend = () => { toastID = $A.toast({title:beforeTitle, fixed: true, timeout: false}); };
}else if (params.beforeSend === true) {
params.beforeSend = () => { toastID = $A.toast({title:loadText, fixed: true, timeout: false}); };
}
if (typeof params.error === 'string') {
errorTitle = params.error;
params.error = () => { $A.toast(errorTitle, "danger"); };
}else if (params.error === true) {
params.error = () => { $A.toast(busyNetwork, "danger"); };
}
if (params.complete === true) {
params.complete = () => { toastID?$A.toast(toastID):'' };
}
}
//
let language = window.localStorage['__language:type__'] || 'en';
if (typeof $A.app === 'object') {
language = $A.app.languageType || language;
}
//
if (typeof params.header !== 'object') params.header = {};
params.header['Content-Type'] = 'application/json';
params.header['language'] = language;
//
params.data['__Access-Control-Allow-Origin'] = true;
params.beforeSend();
params.before();
$A.ihttp({
url: params.url,
data: params.data,
@ -1333,15 +1282,15 @@
crossDomain: true,
dataType: params.dataType,
timeout: params.timeout,
success: function(data, status, xhr) {
success: function (data, status, xhr) {
params.complete();
params.success(data, status, xhr);
params.afterComplete(true);
params.after(true);
},
error: function(xhr, status) {
error: function (xhr, status) {
params.complete();
params.error(xhr, status);
params.afterComplete(false);
params.after(false);
}
});
}

View File

@ -35,32 +35,28 @@
},
apiAjax(params) {
if (typeof params !== 'object') return false;
if (!$A.isJson(params)) return false;
if (typeof params.success === 'undefined') params.success = () => { };
if (typeof params.header !== 'object') params.header = {};
if (typeof params.header === 'undefined') params.header = {};
params.url = this.apiUrl(params.url);
params.header['token'] = $A.app.$store.state.userToken;
params.header['Content-Type'] = 'application/json';
params.header['language'] = $A.getLanguage();
params.header['token'] = $A.store.state.userToken;
//
let beforeCall = params.beforeSend;
params.beforeSend = () => {
let beforeCall = params.before;
params.before = () => {
$A.aAjaxLoadNum++;
$A(".w-spinner").show();
//
if (typeof beforeCall == "function") {
beforeCall();
}
$A(".common-spinner").show();
typeof beforeCall == "function" && beforeCall();
};
//
let completeCall = params.complete;
params.complete = () => {
$A.aAjaxLoadNum--;
if ($A.aAjaxLoadNum <= 0) {
$A(".w-spinner").hide();
}
//
if (typeof completeCall == "function") {
completeCall();
$A(".common-spinner").hide();
}
typeof completeCall == "function" && completeCall();
};
//
let callback = params.success;
@ -71,7 +67,7 @@
$A.modalError({
content: data.msg,
onOk: () => {
$A.userLogout();
$A.logout();
}
});
return;
@ -79,7 +75,7 @@
if (data.ret === -2 && params.role !== false) {
//没有权限
$A.modalError({
content: data.msg ? data.msg : "你没有相关的权限查看或编辑!"
content: data.msg || "你没有相关的权限查看或编辑!"
});
}
}
@ -96,46 +92,47 @@
if (WListener) {
WListener.complete();
WListener.error("timeout");
WListener.afterComplete();
WListener.after();
}
}, params.timeout || 30000);
$A.aAjaxWsListener.push({
apiWebsocket: apiWebsocket,
complete: typeof params.complete === "function" ? params.complete : () => { },
afterComplete: typeof params.afterComplete === "function" ? params.afterComplete : () => { },
after: typeof params.after === "function" ? params.after : () => { },
success: typeof params.success === "function" ? params.success : () => { },
error: typeof params.error === "function" ? params.error : () => { },
});
//
params.complete = () => { };
params.afterComplete = () => { };
params.after = () => { };
params.success = () => { };
params.error = () => { };
params.header['Api-Websocket'] = apiWebsocket;
//
if ($A.aAjaxWsReady === false) {
$A.aAjaxWsReady = true;
$A.WSOB.setOnMsgListener("apiWebsocket", [
'apiWebsocket',
], (msgDetail) => {
switch (msgDetail.messageType) {
case 'apiWebsocket':
clearTimeout(apiTimeout);
const apiWebsocket = msgDetail.apiWebsocket;
const apiSuccess = msgDetail.apiSuccess;
const apiResult = msgDetail.body;
const WListener = $A.aAjaxWsListener.find((item) => item.apiWebsocket == apiWebsocket);
$A.aAjaxWsListener = $A.aAjaxWsListener.filter((item) => item.apiWebsocket != apiWebsocket);
if (WListener) {
WListener.complete();
if (apiSuccess) {
WListener.success(apiResult);
} else {
WListener.error(apiResult);
$A.store.commit("wsMsgListener", {
name: "apiWebsocket",
callback: (msg) => {
switch (msg.type) {
case 'apiWebsocket':
clearTimeout(apiTimeout);
const apiWebsocket = msg.apiWebsocket;
const apiSuccess = msg.apiSuccess;
const apiResult = msg.data;
const WListener = $A.aAjaxWsListener.find((item) => item.apiWebsocket == apiWebsocket);
$A.aAjaxWsListener = $A.aAjaxWsListener.filter((item) => item.apiWebsocket != apiWebsocket);
if (WListener) {
WListener.complete();
if (apiSuccess) {
WListener.success(apiResult);
} else {
WListener.error(apiResult);
}
WListener.after();
}
WListener.afterComplete();
}
break;
break;
}
}
});
}
@ -148,454 +145,13 @@
aAjaxWsListener: [],
/**
* 打开登录页面
* 登出打开登录页面
*/
userLogout() {
logout() {
const from = window.location.pathname == '/' ? '' : encodeURIComponent(window.location.href);
$A.app.$store.commit('setUserInfo', {});
$A.app.goForward({path: '/login', query: from ? {from: from} : {}}, true);
$A.store.commit('setUserInfo', {});
$A.goForward({path: '/login', query: from ? {from: from} : {}}, true);
},
/**
* 权限是否通过
* @param role
* @returns {boolean}
*/
identityCheck(role) {
const userInfo = $A.app.$store.state.userInfo;
return $A.identityRaw(role, userInfo.identity);
},
/**
* 权限是否通过
* @param role
* @param identity
* @returns {boolean}
*/
identityRaw(role, identity) {
let isRole = false;
$A.each(identity, (index, res) => {
if (res === role) {
isRole = true;
}
});
return isRole;
},
});
/**
* =============================================================================
* **************************** websocket assist ***************************
* =============================================================================
*/
$.extend({
/**
* @param config {userid, url, token, logCallback}
*/
WTWS: function (config) {
this.__instance = null;
this.__connected = false;
this.__callbackid = {};
this.__openNum = 0;
this.__autoNum = 0;
this.__lastSend = 0;
this.__autoLine = function (timeout) {
var tempNum = this.__autoNum;
var thas = this;
setTimeout(function () {
if (tempNum === thas.__autoNum) {
thas.__autoNum++
if (!thas.__config.token) {
thas.__log("[WS] No token");
thas.__autoLine(timeout);
} else {
// 发refresh之前判断10秒内没有使用过sendTo的再发送refresh
if (thas.__lastSend + 10 < Math.round(new Date().getTime() / 1000)) {
thas.sendTo('refresh', function (res) {
thas.__log("[WS] Connection " + (res.status ? 'success' : 'error'));
thas.__autoLine(timeout);
});
}
}
}
}, Math.min(timeout, 30) * 1000);
}
this.__log = function (text, event) {
typeof this.__config.logCallback === "function" && this.__config.logCallback(text, event);
}
this.__lExists = function (string, find, lower) {
string += "";
find += "";
if (lower !== true) {
string = string.toLowerCase();
find = find.toLowerCase();
}
return (string.substring(0, find.length) === find);
}
this.__rNum = function (str, fixed) {
var _s = Number(str);
if (_s + "" === "NaN") {
_s = 0;
}
if (/^[0-9]*[1-9][0-9]*$/.test(fixed)) {
_s = _s.toFixed(fixed);
var rs = _s.indexOf('.');
if (rs < 0) {
_s += ".";
for (var i = 0; i < fixed; i++) {
_s += "0";
}
}
}
return _s;
}
this.__jParse = function (str, defaultVal) {
if (str === null) {
return defaultVal ? defaultVal : {};
}
if (typeof str === "object") {
return str;
}
try {
return JSON.parse(str);
} catch (e) {
return defaultVal ? defaultVal : {};
}
}
this.__randString = function (len) {
len = len || 32;
var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678oOLl9gqVvUuI1';
var maxPos = $chars.length;
var pwd = '';
for (var i = 0; i < len; i++) {
pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
}
this.__urlParams = function(url, params) {
if (typeof params === "object" && params !== null) {
url+= "";
url+= url.indexOf("?") === -1 ? '?' : '';
for (var key in params) {
if (!params.hasOwnProperty(key)) {
continue;
}
url+= '&' + key + '=' + params[key];
}
}
return url.replace("?&", "?");
}
this.__isArr = function (obj){
return Object.prototype.toString.call(obj)=='[object Array]';
}
/**
* 设置参数
* @param config
*/
this.config = function (config) {
if (typeof config !== "object" || config === null) {
config = {};
}
config.userid = config.userid || '';
config.url = config.url || '';
config.token = config.token || '';
config.logCallback = config.logCallback || null;
this.__config = config;
return this;
}
/**
* 连接
* @param force
*/
this.connection = function (force) {
if (!this.__lExists(this.__config.url, "ws://") && !this.__lExists(this.__config.url, "wss://")) {
this.__log("[WS] No connection address");
return this;
}
if (!this.__config.token) {
this.__log("[WS] No connected token");
return this;
}
if (this.__instance !== null && force !== true) {
this.__log("[WS] Connection exists");
return this;
}
var thas = this;
// 初始化客户端套接字并建立连接
this.__instance = new WebSocket(this.__urlParams(this.__config.url, {
mode: 'console',
token: this.__config.token,
}));
// 连接建立时触发
this.__instance.onopen = function (event) {
thas.__log("[WS] Connection opened", event);
}
// 接收到服务端推送时执行
this.__instance.onmessage = function (event) {
thas.__log("[WS] Message", event);
var msgDetail = thas.__jParse(event.data);
if (msgDetail.messageType === 'open') {
thas.__log("[WS] Connection connected");
msgDetail.openNum = thas.__openNum;
msgDetail.config = thas.__config;
thas.__openNum++;
thas.__connected = true;
thas.__autoLine(30);
} else if (msgDetail.messageType === 'back') {
typeof thas.__callbackid[msgDetail.messageId] === "function" && thas.__callbackid[msgDetail.messageId](msgDetail.body);
delete thas.__callbackid[msgDetail.messageId];
return;
}
if (thas.__rNum(msgDetail.contentId) > 0) {
thas.sendTo('roger', msgDetail.contentId);
}
thas.triggerMsgListener(msgDetail);
};
// 连接关闭时触发
this.__instance.onclose = function (event) {
thas.__log("[WS] Connection closed", event);
thas.__connected = false;
thas.__instance = null;
thas.__autoLine(3);
}
// 连接出错
this.__instance.onerror = function (event) {
thas.__log("[WS] Connection error", event);
thas.__connected = false;
thas.__instance = null;
thas.__autoLine(3);
}
return this;
}
/**
* 添加消息监听
* @param listenerName
* @param listenerType
* @param callback
*/
this.setOnMsgListener = function (listenerName, listenerType, callback) {
if (typeof listenerName != "string") {
return this;
}
if (typeof listenerType === "function") {
callback = listenerType;
listenerType = [];
}
if (!this.__isArr(listenerType)) {
listenerType = [listenerType];
}
if (typeof callback === "function") {
window.webSocketConfig.LISTENER[listenerName] = {
callback: callback,
listenerType: listenerType,
}
}
return this;
}
this.triggerMsgListener = function (msgDetail) {
var key, item;
for (key in window.webSocketConfig.LISTENER) {
if (!window.webSocketConfig.LISTENER.hasOwnProperty(key)) {
continue;
}
item = window.webSocketConfig.LISTENER[key];
if (item.listenerType.length > 0 && item.listenerType.indexOf(msgDetail.messageType) === -1) {
continue;
}
if (typeof item.callback === "function") {
try {
item.callback(msgDetail);
} catch (e) { }
}
}
}
/**
* 发送消息
* @param messageType 会话类型
* - refresh: 刷新
* @param target 发送目标
* @param body 发送内容对象或数组
* @param callback 发送回调
* @param againNum
*/
this.sendTo = function (messageType, target, body, callback, againNum = 0) {
if (typeof target === "object" && typeof body === "undefined") {
body = target;
target = null;
}
if (typeof target === "function") {
body = target;
target = null;
}
if (typeof body === "function") {
callback = body;
body = null;
}
if (body === null || typeof body !== "object") {
body = {};
}
//
var thas = this;
if (this.__instance === null || this.__connected === false) {
if (againNum < 10 && messageType != 'team') {
setTimeout(function () {
thas.sendTo(messageType, target, body, callback, thas.__rNum(againNum) + 1)
}, 600);
if (againNum === 0) {
this.connection();
}
} else {
if (this.__instance === null) {
this.__log("[WS] Service not connected");
typeof callback === "function" && callback({status: 0, message: '服务未连接'});
} else {
this.__log("[WS] Failed connection");
typeof callback === "function" && callback({status: 0, message: '未连接成功'});
}
}
return this;
}
if (['refresh', 'notificationStatus'].indexOf(messageType) === -1) {
this.__log("[WS] Wrong message messageType: " + messageType);
typeof callback === "function" && callback({status: 0, message: '错误的消息类型: ' + messageType});
return this;
}
//
var contentId = 0;
if (messageType === 'roger') {
contentId = target;
target = null;
}
var messageId = '';
if (typeof callback === "function") {
messageId = this.__randString(16);
this.__callbackid[messageId] = callback;
}
this.__lastSend = Math.round(new Date().getTime()/1000);
this.__instance.send(JSON.stringify({
messageType: messageType,
messageId: messageId,
contentId: contentId,
userid: this.__config.userid,
target: target,
body: body,
time: this.__lastSend,
}));
return this;
}
/**
* 关闭连接
*/
this.close = function () {
if (this.__instance === null) {
this.__log("[WS] Service not connected");
return this;
}
if (this.__connected === false) {
this.__log("[WS] Failed connection");
return this;
}
this.__instance.close();
return this;
}
return this.config(config);
},
WSOB: {
instance: null,
isClose: false,
/**
* 初始化
*/
initialize() {
let url = window.webSocketConfig.URL;
if (!url) {
url = window.location.origin;
url = url.replace("https://", "wss://");
url = url.replace("http://", "ws://");
url+= "/ws";
}
let config = {
userid: $A.app.$store.state.userInfo.userid,
url: url,
token: $A.app.$store.state.userToken,
};
if (window.webSocketConfig.DEBUG) {
config.logCallback = function (msg) {
console.log(msg);
};
}
if (this.instance === null) {
this.instance = new $A.WTWS(config);
this.instance.connection()
} else {
this.instance.config(config);
if (this.isClose) {
this.isClose = false
this.instance.connection();
}
}
},
/**
* 主动连接
*/
connection() {
this.initialize();
this.instance.connection();
},
/**
* 监听消息
* @param listenerName
* @param listenerType
* @param callback
*/
setOnMsgListener(listenerName, listenerType, callback) {
this.initialize();
this.instance.setOnMsgListener(listenerName, listenerType, callback);
},
/**
* 发送消息
* @param messageType
* @param target
* @param body
* @param callback
*/
sendTo(messageType, target, body, callback) {
this.initialize();
this.instance.sendTo(messageType, target, body, callback);
},
/**
* 关闭连接
*/
close() {
if (this.instance === null) {
return;
}
this.isClose = true
this.instance.config(null).close();
},
}
});
/**
@ -611,10 +167,10 @@
content: config
};
}
config.title = $A.app.$L(config.title || (typeof config.render === 'undefined' ? '温馨提示' : ''));
config.content = $A.app.$L(config.content || '');
config.okText = $A.app.$L(config.okText || '确定');
config.cancelText = $A.app.$L(config.cancelText || '取消');
config.title = $A.L(config.title || (typeof config.render === 'undefined' ? '温馨提示' : ''));
config.content = $A.L(config.content || '');
config.okText = $A.L(config.okText || '确定');
config.cancelText = $A.L(config.cancelText || '取消');
return config;
},
@ -623,7 +179,7 @@
setTimeout(() => { $A.modalConfirm(config) }, millisecond);
return;
}
$A.app.$Modal.confirm($A.modalConfig(config));
$A.Modal.confirm($A.modalConfig(config));
},
modalSuccess(config, millisecond = 0) {
@ -631,7 +187,7 @@
setTimeout(() => { $A.modalSuccess(config) }, millisecond);
return;
}
$A.app.$Modal.success($A.modalConfig(config));
$A.Modal.success($A.modalConfig(config));
},
modalInfo(config, millisecond = 0) {
@ -639,7 +195,7 @@
setTimeout(() => { $A.modalInfo(config) }, millisecond);
return;
}
$A.app.$Modal.info($A.modalConfig(config));
$A.Modal.info($A.modalConfig(config));
},
modalWarning(config, millisecond = 0) {
@ -647,7 +203,7 @@
setTimeout(() => { $A.modalWarning(config) }, millisecond);
return;
}
$A.app.$Modal.warning($A.modalConfig(config));
$A.Modal.warning($A.modalConfig(config));
},
modalError(config, millisecond = 0) {
@ -655,7 +211,7 @@
setTimeout(() => { $A.modalError(config) }, millisecond);
return;
}
$A.app.$Modal.error($A.modalConfig(config));
$A.Modal.error($A.modalConfig(config));
},
modalInfoShow(title, data, addConfig) {
@ -670,30 +226,30 @@
let config = {
title: title,
content: content,
okText: $A.app.$L('关闭'),
okText: $A.L('关闭'),
closable: true
};
if (typeof addConfig == 'object' && addConfig) {
config = Object.assign(config, addConfig);
}
this.app.$Modal.info(config);
this.Modal.info(config);
},
modalAlert(msg) {
alert($A.app.$L(msg));
alert($A.L(msg));
},
//提示
messageSuccess(msg) {
$A.app.$Message.success($A.app.$L(msg));
$A.Message.success($A.L(msg));
},
messageWarning(msg) {
$A.app.$Message.warning($A.app.$L(msg));
$A.Message.warning($A.L(msg));
},
messageError(msg) {
$A.app.$Message.error($A.app.$L(msg));
$A.Message.error($A.L(msg));
},
//通知
@ -703,17 +259,17 @@
desc: config
};
}
config.title = $A.app.$L(config.title || (typeof config.render === 'undefined' ? '温馨提示' : ''));
config.desc = $A.app.$L(config.desc || '');
config.title = $A.L(config.title || (typeof config.render === 'undefined' ? '温馨提示' : ''));
config.desc = $A.L(config.desc || '');
return config;
},
noticeSuccess(config) {
$A.app.$Notice.success($A.noticeConfig(config));
$A.Notice.success($A.noticeConfig(config));
},
noticeWarning(config) {
$A.app.$Notice.warning($A.noticeConfig(config));
$A.Notice.warning($A.noticeConfig(config));
},
noticeError(config) {
@ -723,7 +279,7 @@
duration: 6
};
}
$A.app.$Notice.error($A.noticeConfig(config));
$A.Notice.error($A.noticeConfig(config));
},
});

View File

@ -24,7 +24,7 @@
</div>
</Tooltip>
</li>
<li :class="['project-icon', $store.state.projectChatShow ? 'active' : '']" @click="$store.commit('toggleProjectChatShow')">
<li :class="['project-icon', $store.state.projectChatShow ? 'active' : '']" @click="$store.commit('toggleBoolean', 'projectChatShow')">
<Icon type="ios-chatbubbles" />
<Badge :count="projectMsgUnread"></Badge>
</li>
@ -41,7 +41,7 @@
</li>
</ul>
<div class="project-switch">
<div :class="['project-switch-button', $store.state.projectListPanel ? 'menu' : '']" @click="$store.commit('toggleProjectListPanel')">
<div :class="['project-switch-button', $store.state.projectListPanel ? 'menu' : '']" @click="$store.commit('toggleBoolean', 'projectListPanel')">
<div><i class="iconfont">&#xe60c;</i></div>
<div><i class="iconfont">&#xe66a;</i></div>
</div>
@ -104,7 +104,7 @@
</div>
<!--我的任务-->
<div :class="['project-table-body', !$store.state.taskMyShow ? 'project-table-hide' : '']">
<div @click="$store.commit('toggleTaskMyShow')">
<div @click="$store.commit('toggleBoolean', 'taskMyShow')">
<Row class="project-row">
<Col span="12" class="row-title">
<i class="iconfont">&#xe689;</i>
@ -162,7 +162,7 @@
</div>
<!--未完成任务-->
<div :class="['project-table-body', !$store.state.taskUndoneShow ? 'project-table-hide' : '']">
<div @click="$store.commit('toggleTaskUndoneShow')">
<div @click="$store.commit('toggleBoolean', 'taskUndoneShow')">
<Row class="project-row">
<Col span="12" class="row-title">
<i class="iconfont">&#xe689;</i>
@ -209,7 +209,7 @@
</div>
<!--已完成任务-->
<div :class="['project-table-body', !$store.state.taskCompletedShow ? 'project-table-hide' : '']">
<div @click="$store.commit('toggleTaskCompletedShow')">
<div @click="$store.commit('toggleBoolean', 'taskCompletedShow')">
<Row class="project-row">
<Col span="12" class="row-title">
<i class="iconfont">&#xe689;</i>

View File

@ -1,47 +1,12 @@
export default {
/**
* 切换项目聊天显隐
* 切换Boolean变量
* @param state
* @param key
*/
toggleProjectChatShow(state) {
state.projectChatShow = !state.projectChatShow
state.setStorage('projectChatShow', state.projectChatShow);
},
/**
* 切换项目面板显示类型
* @param state
*/
toggleProjectListPanel(state) {
state.projectListPanel = !state.projectListPanel
state.setStorage('projectListPanel', state.projectListPanel);
},
/**
* 切换项目面板显示显示我的任务
* @param state
*/
toggleTaskMyShow(state) {
state.taskMyShow = !state.taskMyShow
state.setStorage('taskMyShow', state.taskMyShow);
},
/**
* 切换项目面板显示显示未完成任务
* @param state
*/
toggleTaskUndoneShow(state) {
state.taskUndoneShow = !state.taskUndoneShow
state.setStorage('taskUndoneShow', state.taskUndoneShow);
},
/**
* 切换项目面板显示显示已完成任务
* @param state
*/
toggleTaskCompletedShow(state) {
state.taskCompletedShow = !state.taskCompletedShow
state.setStorage('taskCompletedShow', state.taskCompletedShow);
toggleBoolean(state, key) {
state[key] = !state[key]
state.method.setStorage('boolean:' + key, state[key]);
},
/**
@ -71,7 +36,7 @@ export default {
$A.apiAjax({
url: 'users/info',
error: () => {
$A.userLogout();
$A.logout();
},
success: ({ret, data, msg}) => {
if (ret === 1) {
@ -89,14 +54,16 @@ export default {
* @param info
*/
setUserInfo(state, info) {
const userInfo = state._cloneJSON(info);
userInfo.userid = state._runNum(userInfo.userid);
const userInfo = state.method.cloneJSON(info);
userInfo.userid = state.method.runNum(userInfo.userid);
userInfo.token = userInfo.userid > 0 ? (userInfo.token || state.userToken) : '';
state.userInfo = userInfo;
state.userId = userInfo.userid;
state.userToken = userInfo.token;
state.setStorage('userInfo', state.userInfo);
state.userIsAdmin = state.method.inArray('admin', userInfo.identity);
state.method.setStorage('userInfo', state.userInfo);
this.commit('getProjectList');
this.commit('wsConnection');
},
/**
@ -131,10 +98,10 @@ export default {
* @param project_id
*/
getProjectDetail(state, project_id) {
if (state._runNum(project_id) === 0) {
if (state.method.runNum(project_id) === 0) {
return;
}
if (state._isJson(state.cacheProject[project_id])) {
if (state.method.isJson(state.cacheProject[project_id])) {
state.projectDetail = state.cacheProject[project_id];
}
state.projectDetail.id = project_id;
@ -173,13 +140,13 @@ export default {
* @param params {userid, success, complete}
*/
getUserBasic(state, params) {
if (!state._isJson(params)) {
if (!state.method.isJson(params)) {
return;
}
const {userid, success, complete} = params;
const time = Math.round(new Date().getTime() / 1000);
const array = [];
(state._isArray(userid) ? userid : [userid]).some((uid) => {
(state.method.isArray(userid) ? userid : [userid]).some((uid) => {
if (state.cacheUserBasic[uid]) {
typeof success === "function" && success(state.cacheUserBasic[uid].data, false);
if (time - state.cacheUserBasic[uid].time <= 10) {
@ -223,5 +190,130 @@ export default {
}
}
});
},
/**
* 初始化 websocket
* @param state
*/
wsConnection(state) {
clearTimeout(state.wsTimeout);
if (state.userId === 0) {
if (state.ws) {
state.ws.close();
state.ws = null;
}
return;
}
let url = window.webSocketConfig.URL;
if (!url) {
url = window.location.origin;
url = url.replace("https://", "wss://");
url = url.replace("http://", "ws://");
url += "/ws";
}
url += "?action=web&token=" + state.userToken;
//
state.ws = new WebSocket(url);
state.ws.onopen = (e) => {
console.log("[WS] Open", e)
};
state.ws.onclose = (e) => {
console.log("[WS] Close", e);
state.ws = null;
//
clearTimeout(state.wsTimeout);
state.wsTimeout = setTimeout(() => {
this.commit('wsConnection');
}, 3000);
};
state.ws.onerror = (e) => {
console.log("[WS] Error", e);
state.ws = null;
//
clearTimeout(state.wsTimeout);
state.wsTimeout = setTimeout(() => {
this.commit('wsConnection');
}, 3000);
};
state.ws.onmessage = (e) => {
console.log("[WS] Message", e);
const msgDetail = state.method.jsonParse(event.data);
const {type, msgId} = msgDetail;
switch (type) {
case "open":
break
case "receipt":
typeof state.wsCall[msgId] === "function" && state.wsCall[msgId](msgDetail.body, true);
delete state.wsCall[msgId];
break
default:
msgId && this.commit('wsSend', {type: 'receipt', msgId});
state.wsMsg = msgDetail;
Object.values(state.wsListener).forEach((call) => {
if (typeof call === "function") {
try {
call(msgDetail);
} catch (err) {
console.log("[WS] Callerr", err);
}
}
})
break
}
}
},
/**
* 发送 websocket 消息
* @param state
* @param params {type, to, data, callback}
*/
wsSend(state, params) {
if (!state.method.isJson(params)) {
return;
}
const {type, to, data, callback} = params;
if (!state.ws) {
typeof callback === "function" && callback(null, false)
return;
}
if (typeof callback === "function") {
params.msgId = state.method.randomString(16)
state.wsCall[params.msgId] = callback;
}
try {
state.ws.send(JSON.stringify({
type,
to,
data
}));
} catch (e) {
typeof callback === "function" && callback(null, false)
}
},
/**
* 监听消息
* @param state
* @param params {name, callback}
*/
wsMsgListener(state, params) {
const {name, callback} = params;
if (typeof callback === "function") {
state.wsListener[name] = callback;
} else {
state.wsListener[name] && delete state.wsListener[name];
}
},
/**
* 关闭 websocket
* @param state
*/
wsClose(state) {
state.ws && state.ws.close();
}
}

View File

@ -1,56 +1,63 @@
const stateCommon = {
const method = {
setStorage(key, value) {
return this._storage(key, value);
return this.storage(key, value);
},
getStorage(key, def = null) {
let value = this._storage(key);
let value = this.storage(key);
return value || def;
},
getStorageString(key, def = '') {
let value = this._storage(key);
let value = this.storage(key);
return typeof value === "string" || typeof value === "number" ? value : def;
},
getStorageNumber(key, def = 0) {
let value = this._storage(key);
let value = this.storage(key);
return typeof value === "number" ? value : def;
},
getStorageBoolean(key, def = false) {
let value = this._storage(key);
let value = this.storage(key);
return typeof value === "boolean" ? value : def;
},
getStorageArray(key, def = {}) {
let value = this._storage(key);
return this._isArray(value) ? value : def;
let value = this.storage(key);
return this.isArray(value) ? value : def;
},
getStorageJson(key, def = {}) {
let value = this._storage(key);
return this._isJson(value) ? value : def;
let value = this.storage(key);
return this.isJson(value) ? value : def;
},
_isArray(obj) {
isArray(obj) {
return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == '[object array]' && typeof obj.length == "number";
},
_isJson(obj) {
isJson(obj) {
return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]" && typeof obj.length == "undefined";
},
_storage(key, value) {
inArray(key, array) {
if (!this.isArray(array)) {
return false;
}
return array.includes(key);
},
storage(key, value) {
let keyName = 'state';
if (typeof value === 'undefined') {
return this._loadFromlLocal('__::', key, '', '__' + keyName + '__');
return this.loadFromlLocal('__::', key, '', '__' + keyName + '__');
} else {
this._savaToLocal('__::', key, value, '__' + keyName + '__');
this.savaToLocal('__::', key, value, '__' + keyName + '__');
}
},
_savaToLocal(id, key, value, keyName) {
savaToLocal(id, key, value, keyName) {
try {
if (typeof keyName === 'undefined') keyName = '__seller__';
let seller = window.localStorage[keyName];
@ -69,7 +76,7 @@ const stateCommon = {
}
},
_loadFromlLocal(id, key, def, keyName) {
loadFromlLocal(id, key, def, keyName) {
try {
if (typeof keyName === 'undefined') keyName = '__seller__';
let seller = window.localStorage[keyName];
@ -86,29 +93,7 @@ const stateCommon = {
}
},
_count(obj) {
try {
if (typeof obj === "undefined") {
return 0;
}
if (typeof obj === "number") {
obj += "";
}
if (typeof obj.length === 'number') {
return obj.length;
} else {
let i = 0, key;
for (key in obj) {
i++;
}
return i;
}
} catch (e) {
return 0;
}
},
_runNum(str, fixed) {
runNum(str, fixed) {
let _s = Number(str);
if (_s + "" === "NaN") {
_s = 0;
@ -126,13 +111,13 @@ const stateCommon = {
return _s;
},
_cloneJSON(myObj) {
if(typeof(myObj) !== 'object') return myObj;
if(myObj === null) return myObj;
return this._jsonParse(this._jsonStringify(myObj))
cloneJSON(myObj) {
if (typeof (myObj) !== 'object') return myObj;
if (myObj === null) return myObj;
return this.jsonParse(this.jsonStringify(myObj))
},
_jsonParse(str, defaultVal) {
jsonParse(str, defaultVal) {
if (str === null) {
return defaultVal ? defaultVal : {};
}
@ -140,47 +125,54 @@ const stateCommon = {
return str;
}
try {
return JSON.parse(str.replace(/\n/g,"\\n").replace(/\r/g,"\\r"));
return JSON.parse(str.replace(/\n/g, "\\n").replace(/\r/g, "\\r"));
} catch (e) {
return defaultVal ? defaultVal : {};
}
},
_jsonStringify(json, defaultVal) {
jsonStringify(json, defaultVal) {
if (typeof json !== 'object') {
return json;
}
try{
try {
return JSON.stringify(json);
}catch (e) {
} catch (e) {
return defaultVal ? defaultVal : "";
}
}
},
};
const projectChatShow = stateCommon.getStorageBoolean('projectChatShow', true);
const projectListPanel = stateCommon.getStorageBoolean('projectListPanel', true);
// 方法类
const state = {
method
};
const taskMyShow = stateCommon.getStorageBoolean('taskMyShow', true);
const taskUndoneShow = stateCommon.getStorageBoolean('taskUndoneShow', true);
const taskCompletedShow = stateCommon.getStorageBoolean('taskCompletedShow', true);
// Boolean变量
[
'projectChatShow', // 项目聊天显示
'projectListPanel', // 项目面板显示类型
'taskMyShow', // 项目面板显示我的任务
'taskUndoneShow', // 项目面板显示未完成任务
'taskCompletedShow' // 项目面板显示已完成任务
].forEach((key) => {
state[key] = state.method.getStorageBoolean('boolean:' + key, true)
})
const userInfo = stateCommon.getStorageJson('userInfo');
const userId = userInfo.userid = stateCommon._runNum(userInfo.userid);
const userToken = userInfo.token;
// 会员信息
state.userInfo = state.method.getStorageJson('userInfo');
state.userId = state.userInfo.userid = state.method.runNum(state.userInfo.userid);
state.userToken = state.userInfo.token;
state.userIsAdmin = state.method.inArray('admin', state.userInfo.identity);
export default Object.assign(stateCommon, {
projectChatShow,
projectListPanel,
taskMyShow,
taskUndoneShow,
taskCompletedShow,
userId,
userInfo,
userToken,
// Websocket
state.ws = null;
state.wsMsg = {};
state.wsCall = {};
state.wsTimeout = null;
state.wsListener = {};
export default Object.assign(state, {
cacheProject: {},
cacheUserBasic: {},