وصلهای برای غیرفعال کردن تنظیمات تغییر ساعت (daylight saving time) در ویندوز: disable_daylight_saving_time.zip
جهت اطلاع!
لغو قانون «تغییر ساعت»
ابوترابی عضو کمیسیون امور داخلی کشور و شوراها: با موافقت نمایندگان مجلس قانون «تغییر ساعت» لغو شد. براساس نظر کارشناسان تغییر ساعت توجیه اقتصادی نداشته و مضرات زیادی نیز دارد. طبق قانون از سال ۱۴۰۱ ساعت قدیم و جدید وجود نخواهد داشت.
ابوترابی عضو کمیسیون امور داخلی کشور و شوراها: با موافقت نمایندگان مجلس قانون «تغییر ساعت» لغو شد. براساس نظر کارشناسان تغییر ساعت توجیه اقتصادی نداشته و مضرات زیادی نیز دارد. طبق قانون از سال ۱۴۰۱ ساعت قدیم و جدید وجود نخواهد داشت.
پ.ن.
این طرح قطعی نشدهاست و این موضوع هفته آینده در نوبت دستور کار صحن مجلس قرار دارد.
خیلی تلاش کردم که معادل هر شب ساعت 2 بامدادش رو درست کنم ولی نشد. لطفا راهنمایی کنید . ممنون
نظرات مطالب
استفاده از عبارات Cron در Quartz.NET
با سلام و تشکر از مطالب مفیدتون . اگر بخواهیم یک Query را هر یک ساعت یک بار اجرا کنیم چگونه امکان پذیر است . منظور ایجاد job در آن به چه صورت خواهد بود؟
خیر. به ساعت آن هم باید دقت کنید. تنظیم شده به 23:59 دقیقه، روز 21 مارس:
نظرات مطالب
ASP.NET MVC #19
آقای نصیری اگر ما اطلاعاتی داشته باشیم که هفته ای یک بار به روز میشوند چه باید کرد؟
در کل آیا میشود مدت زمان کشینگ را به ساعت بدهیم؟
در کل آیا میشود مدت زمان کشینگ را به ساعت بدهیم؟
نظرات نظرسنجیها
ساعت کاری محل کار شما چگونه است؟
در این نظر سنجی بیشتر بررسی ساعت کاری در شرکتها مد نظر بوده تا نحوی انجام کار.
پاسخ به بازخوردهای پروژهها
ارسال به JsonResult
- محتوای باینری فایل نهایی، یا خروجی byte array آن، باید تبدیل به base64 شود، تا فرمت آن قابلیت قرارگیری در فایل متنی JSON را پیدا کند.
- خروجی باینری قرار گرفته در یک فایل JSON در اصل متنی، هیچ کاربردی ندارد؛ بجز راهی برای انتقال اطلاعات به یک برنامه دیگر (آنهم با سربار بالا).
- اگر کاربر سادهی یک وب سایت قرار است اطلاعاتی را دریافت کند، با استفاده از Ajax نمیتواند فایلی را از سرور دریافت کند. نمونههای مشابه:
- «ایجاد لینک دانلود با استفاده از Handler»
- «مشکل در دریافت خروجی pdf به صورت FlushInBrowser »
- « بازگرداندن Stream فایل از WCF »
راه حل پیشنهادی:
« jquery.fileDownload »
- خروجی باینری قرار گرفته در یک فایل JSON در اصل متنی، هیچ کاربردی ندارد؛ بجز راهی برای انتقال اطلاعات به یک برنامه دیگر (آنهم با سربار بالا).
- اگر کاربر سادهی یک وب سایت قرار است اطلاعاتی را دریافت کند، با استفاده از Ajax نمیتواند فایلی را از سرور دریافت کند. نمونههای مشابه:
- «ایجاد لینک دانلود با استفاده از Handler»
- «مشکل در دریافت خروجی pdf به صورت FlushInBrowser »
- « بازگرداندن Stream فایل از WCF »
راه حل پیشنهادی:
« jquery.fileDownload »
پشتیبانی آنلاین سایت، روشی مناسب برای افزایش سطح تماس مشتریان با فروشندگان، برای جلوگیری از اتلاف وقت در برقراری تماس میباشد.
قصد داریم در این بخش پشتیبانی آنلاین سایت را با استفاده از AngularJs /Asp.Net Mvc / Signalr تهیه کنیم.
امکانات این برنامه:
* امکان مکالمه متنی به همراه ارسال شکلک
* امکان انتقال مکالمه
* مشاهده آرشیو گفتگوها
* امکان ارسال فایل (بزودی)
* امکان ذخیره گفتگو و ارسال گفتگو به ایمیل (بزودی)
* امکان ارسال تیکت در صورت آفلاین بودن کارشناسان (بزودی)
* رعایت مسائل امنیتی(بزودی)
مراحل نحوه اجرای برنامه:
1- باز کردن دو tab، یکی برای کارشناس یکی برای مشتری .
2- تعدادی کارشناس تعریف شده است که با کلیک بر روی هر کدام وارد پنل کارشناس خواهیم شد.
3- شروع مکالمه توسط مشتری با کلیک بر روی chatbox پایین صفحه (سمت راست پایین).
4- شروع کردن مکالمه توسط کارشناس.
5- ادامه،خاتمه یا انتقال مکالمه توسط کارشناس.
نصب کتابخانههای زیر:
//client Install-Package angularjs Install-Package angular-strap Install-Package Microsoft.AspNet.SignalR.JS install-package AngularJs.SignalR.Hub Install-Package jQuery.TimeAgo Install-Package FontAwesome Install-Package toastr Install-Package Twitter.Bootstrap.RTL bower install angular-smilies //server Install-Package Newtonsoft.Json Install-Package Microsoft.AspNet.SignalR Install-Package EntityFramework
گامهای برنامه:
1-ایجاد جداول
جدول Message: هر پیام دارای فرستنده و گیرندهای، به همراه زمان ارسال میباشد.
جدول Session: شامل لیستی از پیامها به همراه ارجاعی به خود (استفاده هنگام انتقال مکالمه )
public partial class Message { public int Id { get; set; } public string Sender { get; set; } public string Receiver { get; set; } public string Body { get; set; } public DateTimeOffset? CreationTime { get; set; } public int? SessionId { get; set; } public virtual Session Session { get; set; } } public partial class Session { public Session() { Messages = new List<Message>(); Sessions = new List<Session>(); } public int Id { get; set; } public string AgentName { get; set; } public string CustomerName { get; set; } public DateTime CreatedDateTime { get; set; } public int? ParentId { get; set; } public virtual Session Parent { get; set; } public virtual ICollection<Message> Messages { get; set; } public virtual ICollection<Session> Sessions { get; set; } }
2- ایجاد ویو مدلهای زیر
public class UserInformation { public string ConnectionId { get; set; } public bool IsOnline { get; set; } public string UserName { get; set; } } public class ChatSessionVm { public string Key { get; set; } public List<string> Value { get; set; } } public class AgentViewModel { public int Id { get; set; } public string CustomerName { get; set; } public int Lenght { get; set; } public DateTimeOffset? Date { get; set; } }
3- ایجاد Hub در سرور
[HubName("chatHub")] public class ChatHub : Microsoft.AspNet.SignalR.Hub { }
listeners متدهای سمت کلاینت
methods آرایه ای از متدهای سمت سرور
در صورت موفقیت آمیز بودن اتصال به هاب، متد init سمت سرور فراخوانی میشود و وضعیت آنلاین بودن کارشناسان برای کلاینت مشخص میشود.
5-وضعیت کارشناسان :
6-مشاهده تقاضای مکالمه کاربران توسط کارشناسان: 7-خاتمه مکالمه توسط کارشناس یا مشتری امکان پذیر میباشد:
8-انتقال مکالمه مشتری به کارشناسی دیگر
فایل agent.html LiveSupport.zip
نکات تکمیلی :
$scope.myHub = new hub("chatHub", { listeners: {}, methods: [] })
$scope.myHub.promise.done(function () { $scope.myHub.init(); $scope.myHub.promise.done(function () { }); });
public void Init() { _chatSessions = _chatSessions ?? (_chatSessions = new List<ChatSessionVm>()); _agents = _agents ?? (_agents = new ConcurrentDictionary<string, UserInformation>()); Clients.Caller.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); }
5-وضعیت کارشناسان :
در صورت آنلاین بودن کارشناسان: ارسال اولین پیام و تقاضای شروع مکالمه
در صورت آفلاین بودن کارشناسان: ارسال تیکت(بزودی)
اگر برای اولین بار پیامی را ارسال میکنید، برای شما session ایی ایجاد نشده است. در اینصورت مکان تقاضای مشتری از سایت http://ipinfo.io دریافت شده و به سرور ارسال میگردد و متد logvist سرور، تقاضای شروع مکالمه مشتری را به اطلاع تمام کارشناسان میرساند و وضعیت chatbox را تغییر میدهد.
اگر session برای مشتری تعریف شده باشد، مکالمه مشتری با کارشناس مربوطه انجام میگردد.
$scope.requestChat = function (msg) { if (!defaultCustomerUserName) { //گرفتن کاربر لاگین شده //ما از آرایه تصادفی استفاده میکنیم var nameDefaultArray = [ 'حسین', 'حسن', 'علی', 'عباس', 'زهرا', 'سمیه' ]; defaultCustomerUserName=nameDefaultArray[Math.floor(Math.random() * nameDefaultArray.length)]; } var userName = defaultCustomerUserName; if (!$scope.chatId) { $scope.chatId = sessionStorage.getItem(chatKey); $http.get("http://ipinfo.io") .success(function (response) { $scope.myHub.logVisit(response.city, response.country, msg, userName); }).error(function (e, status, headers, config) { $scope.myHub.logVisit("Tehran", "Ir", msg, userName) }); $scope.myHub.requestChat(msg); $scope.chatTitle = $scope.options.waitingForOperator; $scope.pendingRequestChat = true; } else { $scope.myHub.clientSendMessage(msg, userName); }; $scope.message = ""; };
6-مشاهده تقاضای مکالمه کاربران توسط کارشناسان:
کارشناسان در صورت تمایل، شروع به مکالمه با کاربر مینمایند و مکالمه آغاز میگردد.با شروع مکالمه توسط کارشناس، متد acceptRequestChat سرور فراخوانی میشود.
پیامهای مناسب برای کارشناس مربوطه، برای مشتری و تمام کارشناسان (به تمام کارشناسان اطلاع داده میشود که مشتری با چه کارشناسی در حال مکالمه میباشد) ارسال میگردد و مقادیر مربوطه در دیتابیس ذخیره میگردد.
public void AcceptRequestChat(string customerConnectionId, string body, string userName) { var agent = FindAgent(Context.ConnectionId); var session = _chatSessions.FirstOrDefault(item => item.Key.Equals(agent.Key)); if (session == null) { _chatSessions.Add(new ChatSessionVm { Key = agent.Key, Value = new List<string> { customerConnectionId } }); } else { session.Value.Add(customerConnectionId); } Clients.Client(Context.ConnectionId).agentChat(customerConnectionId, body, userName); Clients.Client(customerConnectionId).clientChat(customerConnectionId, agent.Value.UserName); foreach (var item in _agents.Where(item => item.Value.IsOnline)) { Clients.Client(item.Value.ConnectionId).refreshChatWith(agent.Value.UserName, customerConnectionId); } var session = _db.Sessions.Add(new Session { AgentName = agent.Key, CustomerName = userName, CreatedDateTime = DateTime.Now }); _db.SaveChanges(); var message = new Message { CreationTime = DateTime.Now, Sender = agent.Key, Receiver = userName, body=body, Session = session }; _db.Messages.Add(message); _db.SaveChanges(); }
متد closeChat سرور فراخوانی میگردد. پیام مناسبی به مشتری و تمام کارشناسان ارسال میگردد.
public void CloseChat(string id) { var findAgent = FindAgent(Context.ConnectionId); var session = _chatSessions.FirstOrDefault(item => item.Value.Contains(id)); if (session == null) return; Clients.Client(id).clientAddMessage(findAgent.Key, "مکالمه شما با کارشناس مربوطه به اتمام رسیده است"); foreach (var agent in _agents) { Clients.Client(agent.Value.ConnectionId).refreshLeaveChat(agent.Value.UserName, id); } _chatSessions.Remove(session); }
8-انتقال مکالمه مشتری به کارشناسی دیگر
مکالمه از کارشناس فعلی گرفته شده و به کارشناس جدید داده میشود؛ به همراه ارسال پیامهای مناسب به طرفهای مربوطه
public void EngageVisitor(string newAgentId, string cumtomerId, string customerName,string clientSessionId) { #region remove session of current agent var currentAgent = FindAgent(Context.ConnectionId); var currentSession = _chatSessions.FirstOrDefault(item => item.Value.Contains(cumtomerId)); if (currentSession != null) { _chatSessions.Remove(currentSession); } #endregion #region add session to new agent var newAgent = FindAgent(newAgentId); var newSession = _chatSessions.FirstOrDefault(item => item.Key.Equals(newAgent.Key)); if (newSession == null) { _chatSessions.Add(new ChatSessionVm { Key = newAgent.Key, Value = new List<string> { cumtomerId } }); } else { newSession.Value.Add(cumtomerId); } #endregion Clients.Client(currentAgent.Value.ConnectionId).addMessage(cumtomerId, newAgent.Key, "ادامه مکالمه به کارشناس " + newAgent.Key + "مقابل منتقل شد"); Clients.Client(newAgentId).addMessage(cumtomerId, currentAgent.Key, "لطفا مکالمه را ادامه دهید.با تشکر"); Clients.Client(cumtomerId).clientAddMessage(newAgent.Value.UserName, "مکالمه شما با کارشناس زیر برقرار گردید" + newAgent.Key); var session = _db.Sessions.FirstOrDefault (item => item.AgentName.Equals(currentAgent.Value.UserName) && item.CustomerName.Equals(customerName)); if (session != null) { var sessionId = session.Id; var messages = _db.Messages.Where(item => item.Session.Id.Equals(sessionId)); var result = JsonConvert.SerializeObject(messages, new Formatting(), _settings); Clients.Client(newAgentId).visitorSwitchConversation (Context.ConnectionId, customerName, result, clientSessionId); } foreach (var item in _agents.Where(item => item.Value.IsOnline)) { Clients.Client(item.Value.ConnectionId).refreshChatWith(newAgent.Value.UserName, cumtomerId); } _db.Sessions.Add(new Session { AgentName = newAgent.Key, CustomerName = customerName, CreatedDateTime = DateTime.Now, Parent = _db.Sessions.Where(item => item.AgentName.Equals(currentAgent.Key) && item.CustomerName.Equals(customerName)).OrderByDescending(item => item.Id).FirstOrDefault() }); _db.SaveChanges(); }
از آنجاییکه اسم متدها کاملا گویا میباشد، به نظر نیازی به توضیح بیشتری ندارند.
فایل کامل app.js
var app = angular.module("app", ["SignalR", 'ngRoute', 'ngAnimate', 'ngSanitize', 'mgcrea.ngStrap', 'angular-smilies']); app.config(["$routeProvider", "$provide", "$httpProvider", "$locationProvider", function ($routeProvider, $provide, $httpProvider, $locationProvider) { $routeProvider. when('/', { templateUrl: 'app/views/home.html', controller: "HomeCtrl" }). when('/agent', { templateUrl: 'app/views/agent.html', controller: "ChatCtrl" }) .otherwise({ redirectTo: "/" });; }]); app.controller("HomeCtrl", ["$scope", function ($scope) { $scope.title = "home"; }]) app.controller("ChatCtrl", ["$scope", "Hub", "$location", "$http", "$rootScope", function ($scope, hub, $location, $http, $rootScope) { if (!$scope.myHub) { var chatKey = "angular-signalr"; var defaultCustomerUserName = null; function getid(id) { var find = false; var position = null; angular.forEach($scope.chatConversation, function (index, i) { if (index.id === id && !find) { find = true; position = i; return; } }); return position; } function apply() { $scope.$apply(); } $scope.boxheader = function () { var height = 0; $("#chat-box").slideToggle('slow', function () { if ($("#chat-box-header").css("bottom") === "0px") { height = $("#chat-box").height() + 20; } else { height = 0; } $("#chat-box-header").css("bottom", height); }); }; var init = function () { $scope.agent = { id: "", name: "", isOnline: false }; $rootScope.msg = ""; $scope.alarmStatus = false; $scope.options = { offlineTitle: "آفلاین", onlineTitle: "آنلاین", waitingForOperator: "لطفا منتظر بمانید تا به اپراتور وصل شوید", emailSent: "ایمیل ارسال گردید", emailFailed: "متاسفانه ایمیل ارسال نگردید", logOut: "خروج", setting: "تنظیمات", conversion: "آرشیو", edit: "ویرایش", alarm: "قطع/وصل کردن صدا", complete: "تکمیل", pending: "منتظر ماندن", reject: "عدم پذیرش", lock: "آنلاین شدن", unlock: "آفلاین شدن", alarmOn: "روشن", alarmOff: "خاموش", upload: "آپلود" }; $scope.chatConversation = []; $scope.chatSessions = []; $scope.customerVisit = []; $scope.agentClientMsgs = []; $scope.clientAgentMsg = []; }(); //تعریف هاب به همراه متدهای آن $scope.myHub = new hub("chatHub", { listeners: { "clientChat": function (id, agentName) { $scope.clientAgentMsg.push({ name: agentName, msg: "با سلام در خدمت میباشم" }); $scope.chatTitle = "کارشناس: " + agentName; $scope.pendingRequestChat = false; sessionStorage.setItem(chatKey, id); }, "agentChat": function (id, firstComment, customerName) { var date = new Date(); var position = getid(id); if (position > 0) { $scope.chatSessions[position].length = $scope.chatConversation[position].length + 1; $scope.chatSessions[position].date = date.toISOString(); return; } else { $scope.chatConversation.push({ id: id, sessions: [{ name: customerName, msg: firstComment, date: date }], agentName: $scope.agent.name, customerName: customerName, dateStartChat: date.getHours() + ":" + date.getMinutes(), }); $scope.chatSessions.push({ id: id, length: 1, userName: customerName, date: date.toISOString() }); } sessionStorage.setItem(chatKey, id); apply(); }, //برروز رسانی لیست برای کارشناسان "refreshChatWith": function (agentName, customerConnectionId) { angular.forEach($scope.customerVisit, function (index, i) { if (index.connectionId === customerConnectionId) { $scope.customerVisit[i].chatWith = agentName; } }); apply(); }, //برروز رسانی لیست برای کارشناسان "refreshLeaveChat": function (agentName, customerConnectionId) { angular.forEach($scope.customerVisit, function (index, i) { if (index.connectionId === customerConnectionId) { $scope.customerVisit[i].chatWith =agentName + "---" + " به مکالمه خاتمه داده است "; } }); apply(); } //وضعیت آنلاین بودن کارشناسان , "onlineStatus": function (state) { if (state) { $scope.chatTitle = $scope.options.onlineTitle; $scope.hasOnline = true; $scope.hasOffline = false; } else { $scope.chatTitle = $scope.options.offlineTitle; $scope.hasOffline = true; $scope.hasOnline = false; } $scope.$apply() }, "loginResult": function (status, id, name) { if (status) { $scope.agent.id = id; $scope.agent.name = name; $scope.agent.isOnline = true; $scope.userIsLogin = $scope.agent; $scope.$apply(function () { $location.path("/agent"); }); } else { $scope.agent = null; toastr.error("کارشناسی با این مشخصات وجود ندارد"); return; } }, "newVisit": function (userName, city, country, chatWith, connectionId, firstComment) { var exist = false; angular.forEach($scope.customerVisit, function (index) { if (index.connectionId === connectionId) { exist = true; return; } }); if (!exist) { var date = new Date(); $scope.customerVisit.unshift({ userName: userName, date: date, city: city, country: country, chatWith: chatWith, connectionId: connectionId, firstComment: firstComment }); if ($scope.alarmStatus) { var snd = new Audio("/App/assets/sounds/Sedna.ogg"); snd.play(); } toastr.success("تقاضای جدید دریافت گردید"); apply(); } }, "addMessage": function (id, from, value) { if ($scope.alarmStatus) { var snd = new Audio("/App/assets/sounds/newmsg.mp3"); snd.play(); } $scope.agentUserMsgs = []; var date = new Date(); var position = getid(id); if ($scope.chatConversation.length > 0 && position != null) { $scope.chatConversation[position].sessions.push({ name: from, msg: value, date: date }); } var item = $scope.chatConversation[position]; if (item) { angular.forEach(item.sessions, function (index) { $scope.agentUserMsgs.push({ name: index.name, msg: index.msg, date: date }); }); $scope.chatSessions[position].length = $scope.chatSessions[position].length + 1; } apply(); }, "clientAddMessage": function (id, from) { if ($scope.alarmStatus) { var snd = new Audio("/App/assets/sounds/newmsg.mp3"); snd.play(); } $scope.clientAgentMsg.push({ name: id, msg: from }); apply(); }, "visitorSwitchConversation": function (id, customerName, sessions, sessionId) { sessions = JSON.parse(sessions); var date = new Date(); var sessionList = []; angular.forEach(sessions, function (index) { sessionList.push({ name: index.sender, msg: index.body, date: index.creationTime }); }); $scope.chatConversation.push({ id: sessionId, sessions: sessionList, customerName: customerName, dateStartChat: date.getHours() + ":" + date.getMinutes(), agentName: $scope.agent.name }); $scope.chatSessions.push({ id: sessionId, length: sessions.length, date: date }); }, "receiveTicket": function (items) { angular.forEach(JSON.parse(items), function (index) { $scope.ticketList = []; $scope.ticketList.push(index); }); }, //آرشیو گفته گوهای کارشناس "receiveHistory": function (items) { $scope.agentHistory = []; angular.forEach(JSON.parse(items), function (index) { $scope.agentHistory.push(index); }); apply(); }, //جزییات آرشیو گفتگوها "detailsHistory": function (items) { $scope.historyMsg = []; angular.forEach(JSON.parse(items), function (index) { $scope.historyMsg.push({ name: index.sender, msg: index.body, date: index.creationTime }); }); $("#detailsAgentHistory").modal(); apply(); }, //لیست کارشناسان آنلاین "agentList": function (items) { $scope.agentList = []; angular.forEach(items, function (index) { if ($scope.agent.name != index.Key) { $scope.agentList.push({ name: index.Key, id: index.Value.ConnectionId }); } }); $("#agentList").modal(); apply(); } }, methods: ["agentConnect", "sendTicket", "requestChat", "clientSendMessage", "closeChat", "init", "logVisit", "agentChangeStatus", "engageVisitor", "agentSendMessage", "transfer", "leaveChat", "acceptRequestChat", "leaveChat", "detailsSessoinMessage", "showAgentList", "getAgentHistoryChat" ], errorHandler: function (error) { console.error(error); } }); $scope.myHub.promise.done(function () { $scope.myHub.init(); $scope.myHub.promise.done(function () { }); }); $scope.LeaveChat = function () { $scope.myHub.LeaveChat(); }; $scope.loginAgent = function (userName) { // username :security user username from agent role if (userName == "hossein" || userName == "ali") { $scope.myHub.promise.done(function () { $scope.myHub.agentConnect(userName).then(function (result) { $scope.agent.name = userName; $scope.agent.isOnline = true; }); }); } }; $scope.requestChat = function (msg) { if (!defaultCustomerUserName) { //گرفتن کاربر لاگین شده //ما از آرایه تصادفی استفاده میکنیم var nameDefaultArray = [ 'حسین', 'حسن', 'علی', 'عباس', 'زهرا', 'سمیه' ]; defaultCustomerUserName=nameDefaultArray[Math.floor(Math.random() * nameDefaultArray.length)]; } var userName = defaultCustomerUserName; if (!$scope.chatId) { $scope.chatId = sessionStorage.getItem(chatKey); $http.get("http://ipinfo.io") .success(function (response) { $scope.myHub.logVisit(response.city, response.country, msg, userName); }).error(function (e, status, headers, config) { $scope.myHub.logVisit("Tehran", "Ir", msg, userName) }); $scope.myHub.requestChat(msg); $scope.chatTitle = $scope.options.waitingForOperator; $scope.pendingRequestChat = true; } else { $scope.myHub.clientSendMessage(msg, userName); }; $scope.message = ""; }; $scope.acceptRequestChat = function (customerConnectionId, firstComment, customerName) { $scope.myHub.acceptRequestChat(customerConnectionId, firstComment, customerName); }; $scope.changeAgentStatus = function () { $scope.agent.isOnline = !$scope.agent.isOnline; $scope.myHub.agentChangeStatus($scope.agent.isOnline); }; $scope.detailsChat = function (chatId, userName) { $scope.agentUserMsgs = []; angular.forEach($scope.chatConversation, function (index) { if (index.id === chatId) { $scope.dateStartChat = index.dateStartChat; angular.forEach(index.sessions, function (value) { $scope.agentUserMsgs.push({ name: value.name, msg: value.msg, date: value.date }); }); }; }); $scope.agentChatWithUser = chatId; $scope.customerName = userName; $("#agentUserChat").modal(); }; $scope.ticket = { submit: function () { var name = $scope.ticket.name; var email = $scope.ticket.email; var comment = $scope.ticket.comment; $scope.myHub.sendTicket(name, email, comment); } }; $scope.showHistory = function () { $scope.myHub.getAgentHistoryChat($scope.agent.name); }; $scope.detailsChatHistory = function (id) { $scope.myHub.detailsSessoinMessage(id, $scope.agent.id); }; $scope.agentMsgToUser = function (msg) { var chatId = $scope.agentChatWithUser; var customerName = $scope.customerName; if (!customerName) { angular.forEach($scope.customerVisit, function (index) { if (index.connectionId == chatId) { customerName = index.userName; } }); } if (chatId !== "" && msg !== "") { $scope.myHub.agentSendMessage(chatId, msg, customerName); } //not bind to scope.msg! not correctly work $scope.msg = ""; $("#post-msg").val(""); }; $scope.closeChat = function (chatId) { var item = $scope.chatConversation[getid(chatId)]; $scope.myHub.closeChat(chatId); }; $scope.engageVisitor = function (newAgentId) { var customerId = $scope.customerId; var customerName = $scope.customerName; var clientSessionId = $scope.clientSessionId; $scope.myHub.engageVisitor(newAgentId, customerId, customerName, clientSessionId); $("[data-dismiss=modal]").trigger({ type: "click" }); }; $scope.selectVisitor = function (customerId, customerName, clientSessionId) { $scope.customerId = customerId; $scope.customerName = customerName; $scope.clientSessionId = clientSessionId; $scope.myHub.showAgentList(); }; $scope.setClass = function (item) { if (item === "من") return "question"; else return "response"; }; $scope.setdirectionClass = function (item) { if (item === $scope.agent.name) return { "float": "left" }; else return { "float": "right" }; }; $scope.setArrowClass = function (item) { if (item === $scope.agent.name) return "left-arrow"; else return "right-arrow"; }; $scope.setAlarm = function () { $scope.alarmStatus = !$scope.alarmStatus; }; } }]); app.directive("showtab", function () { return { link: function (scope, element, attrs) { element.click(function (e) { e.preventDefault(); $(element).addClass("active"); $(element).tab("show"); }); } }; }); //زمان ارسال پیام app.directive("timeAgo", function ($q) { return { restrict: "AE", scope: false, link: function (scope, element, attrs) { jQuery.timeago.settings.strings = { prefixAgo: null, prefixFromNow: null, suffixAgo: "پیش", suffixFromNow: "از حالا", seconds: "کمتر از یک دقیقه", minute: "در حدود یک دقیقه", minutes: "%d دقیقه", hour: "حدود یگ ساعت", hours: "حدود %d ساعت ", day: "یک روز", days: "%d روز", month: "حدود یک ماه", months: "%d ماه", year: "حدود یک سال", years: "%d سال", wordSeparator: " ", numbers: [] } var parsedDate = $q.defer(); parsedDate.promise.then(function () { jQuery(element).timeago(); }); attrs.$observe("title", function (newValue) { parsedDate.resolve(newValue); }); } }; });
فایل chathub.cs
برای آشنایی بیشتر مقاله نگاهی به SignalR Hubs مفید خواهد بود.
[HubName("chatHub")] public class ChatHub : Microsoft.AspNet.SignalR.Hub { private readonly ApplicationDbContext _db = new ApplicationDbContext(); private static ConcurrentDictionary<string, UserInformation> _agents; private static List<ChatSessionVm> _chatSessions; private readonly JsonSerializerSettings _settings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver(), ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; public void Init() { _chatSessions = _chatSessions ?? (_chatSessions = new List<ChatSessionVm>()); _agents = _agents ?? (_agents = new ConcurrentDictionary<string, UserInformation>()); Clients.Caller.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); } public void AgentConnect(string userName) { //ما برای ساده کردن مقایسه ساده ای انجام دادیم فقط کاربر حسین یا علی میتواند کارشناس باشد if (userName == "hossein" || userName == "ali") { var agent = new UserInformation(); if (_agents.Any(item => item.Key == userName)) { agent = _agents[userName]; agent.ConnectionId = Context.ConnectionId; } else { agent.ConnectionId = Context.ConnectionId; agent.UserName = userName; agent.IsOnline = true; _agents.TryAdd(userName, agent); } Clients.Caller.loginResult(true, agent.ConnectionId, agent.UserName); Clients.All.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); } else { Clients.Caller.loginResult(false, null, null); } } public void AgentChangeStatus(bool status) { var agent = _agents.FirstOrDefault(x => x.Value.ConnectionId == Context.ConnectionId).Value; if (agent == null) return; agent.IsOnline = status; Clients.All.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); } public void LogVisit(string city, string country, string firstComment, string userName) { foreach (var agent in _agents) { Clients.Client(agent.Value.ConnectionId).newVisit(userName, city, country, null, Context.ConnectionId, firstComment); } } public void AcceptRequestChat(string customerConnectionId, string body, string userName) { var agent = FindAgent(Context.ConnectionId); var session = _chatSessions.FirstOrDefault(item => item.Key.Equals(agent.Key)); if (session == null) { _chatSessions.Add(new ChatSessionVm { Key = agent.Key, Value = new List<string> { customerConnectionId } }); } else { session.Value.Add(customerConnectionId); } Clients.Client(Context.ConnectionId).agentChat(customerConnectionId, body, userName); Clients.Client(customerConnectionId).clientChat(customerConnectionId, agent.Value.UserName); foreach (var item in _agents.Where(item => item.Value.IsOnline)) { Clients.Client(item.Value.ConnectionId).refreshChatWith(agent.Value.UserName, customerConnectionId); } _db.Sessions.Add(new Session { AgentName = agent.Key, CustomerName = userName, CreatedDateTime = DateTime.Now }); _db.SaveChanges(); var message = new Message { CreationTime = DateTime.Now, Sender = agent.Key, Receiver = userName, Body = body, //ConnectionId = _agents.FirstOrDefault(item => item.Value.UserName == userName).Key, Session = _db.Sessions.OrderByDescending(item => item.Id) .FirstOrDefault(item => item.AgentName.Equals(agent.Key) && item.CustomerName.Equals(userName)) }; _db.Messages.Add(message); _db.SaveChanges(); } public void GetAgentHistoryChat(string userName) { var dic = new Dictionary<int, int>(); var lenght = 0; var chats = _db.Sessions.OrderBy(item => item.Id).Include(item => item.Parent) .Where(item => item.AgentName.Equals(userName)).ToList(); foreach (var session in chats) { Result(session, ref lenght); dic.Add(session.Id, lenght); lenght = 0; } if (!chats.Any()) return; var historyResult = chats.Select(item => new AgentViewModel { Id = item.Id, CustomerName = item.CustomerName, Date = item.CreatedDateTime, Lenght = dic.Any(di => di.Key.Equals(item.Id)) ? dic.FirstOrDefault(di => di.Key.Equals(item.Id)).Value : 0, }).OrderByDescending(item => item.Id).ToList(); Clients.Caller.receiveHistory(JsonConvert.SerializeObject(historyResult, new Formatting(), _settings)); } public void DetailsSessoinMessage(int sessionId, string agentId) { var session = _db.Sessions.FirstOrDefault(item => item.Id.Equals(sessionId)); if (session == null) return; var list = new List<Message>(); GetAllMessages(session, list); var result = JsonConvert.SerializeObject(list.OrderBy(item => item.Id), new Formatting(), _settings); Clients.Client(Context.ConnectionId).detailsHistory(result); } public void ClientSendMessage(string body, string userName) { var session = _chatSessions.FirstOrDefault(item => item.Value.Contains(Context.ConnectionId)); if (session == null || session.Key == null) return; var agentId = _agents.FirstOrDefault(item => item.Key.Equals(session.Key)).Value.ConnectionId; Clients.Caller.clientAddMessage("من", body); Clients.Client(agentId).addMessage(Context.ConnectionId, userName, body); var message = new Message { Sender = FindAgent(agentId).Key, Receiver = userName, Body = body, CreationTime = DateTime.Now, Session = FindSession(userName, FindAgent(agentId).Key) }; _db.Messages.Add(message); _db.SaveChanges(); } public void AgentSendMessage(string id, string body, string userName) { var agent = FindAgent(Context.ConnectionId); Clients.Caller.addMessage(id, agent.Value.UserName, body); Clients.Client(id).clientAddMessage(agent.Value.UserName, body); var message = new Message { Sender = agent.Key, Receiver = userName, Body = body, Session = FindSession(agent.Key, userName), CreationTime = DateTime.Now }; _db.Messages.Add(message); _db.SaveChanges(); } public void CloseChat(string id) { var findAgent = FindAgent(Context.ConnectionId); var session = _chatSessions.FirstOrDefault(item => item.Value.Contains(id)); if (session == null) return; Clients.Client(id).clientAddMessage(findAgent.Key, "مکالمه شما با کارشناس مربوطه به اتمام رسیده است"); foreach (var agent in _agents) { Clients.Client(agent.Value.ConnectionId).refreshLeaveChat(agent.Value.UserName, id); } _chatSessions.Remove(session); } public void RequestChat(string message) { Clients.Caller.clientAddMessage("من", message); } public void EngageVisitor(string newAgentId, string cumtomerId, string customerName,string clientSessionId) { #region remove session of current agent var currentAgent = FindAgent(Context.ConnectionId); var currentSession = _chatSessions.FirstOrDefault(item => item.Value.Contains(cumtomerId)); if (currentSession != null) { _chatSessions.Remove(currentSession); } #endregion #region add session to new agent var newAgent = FindAgent(newAgentId); var newSession = _chatSessions.FirstOrDefault(item => item.Key.Equals(newAgent.Key)); if (newSession == null) { _chatSessions.Add(new ChatSessionVm { Key = newAgent.Key, Value = new List<string> { cumtomerId } }); } else { newSession.Value.Add(cumtomerId); } #endregion Clients.Client(currentAgent.Value.ConnectionId).addMessage(cumtomerId, newAgent.Key, "ادامه مکالمه به کارشناس " + newAgent.Key + "مقابل منتقل شد"); Clients.Client(newAgentId).addMessage(cumtomerId, currentAgent.Key, "لطفا مکالمه را ادامه دهید.با تشکر"); Clients.Client(cumtomerId).clientAddMessage(newAgent.Value.UserName, "مکالمه شما با کارشناس زیر برقرار گردید" + newAgent.Key); var session = _db.Sessions.FirstOrDefault (item => item.AgentName.Equals(currentAgent.Value.UserName) && item.CustomerName.Equals(customerName)); if (session != null) { var sessionId = session.Id; var messages = _db.Messages.Where(item => item.Session.Id.Equals(sessionId)); var result = JsonConvert.SerializeObject(messages, new Formatting(), _settings); Clients.Client(newAgentId).visitorSwitchConversation (Context.ConnectionId, customerName, result, clientSessionId); } foreach (var item in _agents.Where(item => item.Value.IsOnline)) { Clients.Client(item.Value.ConnectionId).refreshChatWith(newAgent.Value.UserName, cumtomerId); } _db.Sessions.Add(new Session { AgentName = newAgent.Key, CustomerName = customerName, CreatedDateTime = DateTime.Now, Parent = _db.Sessions.Where(item => item.AgentName.Equals(currentAgent.Key) && item.CustomerName.Equals(customerName)).OrderByDescending(item => item.Id).FirstOrDefault() }); _db.SaveChanges(); } public void ShowAgentList() { Clients.Caller.agentList(_agents.ToList()); } public override Task OnDisconnected(bool stopCalled) { var id = Context.ConnectionId; var isAgent = _agents != null && _agents.Any(item => item.Value.ConnectionId.Equals(id)); if (isAgent) { UserInformation agent; var currentAgentConnectionId = FindAgent(id).Key; if (currentAgentConnectionId == null) return base.OnDisconnected(stopCalled); if (_chatSessions.Any()) { var sessions = _chatSessions.FirstOrDefault(item => item.Key.Equals(currentAgentConnectionId)); //اطلاع دادن به تمام کاربرانی که در حال مکالمه با کارشناس هستند if (sessions != null) { var result = sessions.Value.ToList(); for (var i = 0; i < result.Count(); i++) { var localId = result[i]; Clients.Client(localId).clientAddMessage(currentAgentConnectionId, "ارتباط شما با مشاور مورد نظر قطع شده است"); } } } _agents.TryRemove(currentAgentConnectionId, out agent); Clients.All.onlineStatus(_agents.Count(x => x.Value.IsOnline) > 0); Clients.Client(id).loginResult(false, null, null); } else { if (_chatSessions == null || !_chatSessions.Any(item => item.Value.Contains(id) && _agents == null)) return base.OnDisconnected(stopCalled); var session = _chatSessions.FirstOrDefault(item => item.Value.Contains(id)); if (session == null) return base.OnDisconnected(stopCalled); var agentName = session.Key; var agent = _agents.FirstOrDefault(item => item.Key.Equals(agentName)); if (agent.Key != null) { Clients.Client(agent.Value.ConnectionId).addMessage(id, "کاربر", "اتصال با کاربر قطع شده است"); } } return base.OnDisconnected(stopCalled); } private KeyValuePair<string, UserInformation> FindAgent(string connectionId) { return _agents.FirstOrDefault(item => item.Value.ConnectionId.Equals(connectionId)); } private Session FindSession(string key, string userName) { return _db.Sessions.Where(item => item.AgentName.Equals(key) && item.CustomerName.Equals(userName)) .OrderByDescending(item => item.Id).FirstOrDefault(); } private static void Result(Session parent, ref int lenght) { while (true) { if (parent == null) return; lenght += parent.Messages.Count(); parent = parent.Parent; } } private static List<Message> GetAllMessages(Session node, List<Message> list) { if (node == null) return null; list.AddRange(node.Messages); if (node.Parent != null) { GetAllMessages(node.Parent, list); } return null; } }
<div> <div> <h2> خوش آمدید <span ng-bind="agent.name"> </span> <a ng-click="changeAgentStatus()"> <i ng-if="changeStatus==null" data-placement="bottom" data-trigger="hover " bs-tooltip="options.lock"></i> <i ng-if="changeStatus==true" data-placement="bottom" data-trigger="hover" bs-tooltip="options.unlock"></i> </a> </h2> <div style="float: left"> <a ng-click="setAlarm()"> <i ng-show="alarmStatus" data-placement="bottom" data-trigger="hover " bs-tooltip="options.alarmOn"></i> <i ng-show="!alarmStatus" data-placement="bottom" data-trigger="hover " bs-tooltip="options.alarmOff"></i> </a> <!--<a data-placement="bottom" data-trigger="hover " bs-tooltip="options.conversion" ng-click="showHistory()"><i></i></a>--> <a data-placement="bottom" data-trigger="hover " bs-tooltip="options.edit"><i></i><span></span></a> <a data-placement="bottom" data-trigger="hover " bs-tooltip="options.setting"><i></i></a> <a data-placement="bottom" data-trigger="hover " bs-tooltip="options.signOut" ng-click="LeaveChat()"><i></i><span></span></a> </div> </div> <div> <div> <div id="chat-content"> <div> <ul> <li> <a showtab href="#online-list">آنلاین</a> </li> <li> <a ng-click="showHistory()" showtab href="#conversation">آرشیو گفتگوها</a> </li> </ul> <div> <div id="online-list"> <div> <h2> <i></i><span></span> <span>نمایش آنلاین مراجعه ها</span> </h2> </div> <div> <div id="agent-chat"> <div id="real-time-visits"> <table id="current-visits"> <thead> <tr> <th>نام کاربر</th> <th>زمان اولین تقاضا</th> <th>منطقه</th> <th>پاسخ</th> </tr> </thead> <tbody> <tr id="{{item.connectionId}}" ng-animate="animate" ng-repeat="item in customerVisit "> <td ng-bind="item.userName"></td> <td> <span time-ago title="{{item.date}}"></span> </td> <td> <span ng-bind="item.country"></span> /<span ng-bind="item.city"> </span> </td> <td> <a style="cursor: pointer" ng-if="item.chatWith== null" ng-click="acceptRequestChat(item.connectionId,item.firstComment,item.userName)"> شروع مکالمه </a> <span ng-if="item.chatWith "> وضعیت: <span>در حال مکالمه با</span> <span ng-bind="item.chatWith"></span> <a ng-show="item.chatWith==agent.name" ng-click="selectVisitor(item.connectionId,item.userName,item.connectionId)"> انتقال مکالمه </a> </span> <ul ng-repeat="session in chatSessions track by $index" style="padding:0px;"> <li ng-if="session.id==item.connectionId" id="{{session.id}}"> <div> <p> تاریخ شروع مکالمه: <span time-ago title="{{session.date}}"></span> </p> <p> تعداد پیام ها: <span ng-bind="session.length"></span> </p> </div> <p> <a ng-click="detailsChat(session.id,session.userName)">جزییات </a> <a ng-click="closeChat(session.id)"> خاتمه عملیات </a> </p> </li> </ul> </td> </tr> </tbody> </table> </div> </div> </div> </div> <div id="conversation"> <div> <h2> <i></i><span></span> <span>آرشیو گفتگوهای </span> {{agent.name}} </h2> </div> <div> <div> <table id="current-visits"> <thead> <tr> <th>شناسه مشتری</th> <th>نام مشتری</th> <th>تعداد محاوره ها</th> <th>تاریخ</th> <th>جزئیات</th> </tr> </thead> <tbody> <tr ng-repeat="item in agentHistory track by $index"> <td ng-bind="item.id"></td> <td ng-bind="item.customerName"></td> <th ng-bind="item.lenght"></th> <td><span time-ago title="{{item.date}}"></span></td> <th> <ang-click="detailsChatHistory(item.id)" >مشاهده جزییات گفتگو</a> </th> </tr> </tbody> </table> </div> </div> </div> </div> </div> </div> </div> </div> <div id="detailsAgentHistory" tabindex="-1" role="dialog" aria-labelledby="cmdLabel" aria-hidden="true"> <div> <div> <div> <div> <button type="button" data-dismiss="modal" aria-hidden="true">×</button> </div> <h2> <span></span>تاریخچه گفتگو </h2> </div> <div> <div style="display: block"> <ul ng-repeat="item in historyMsg"> <li> <span ng-bind="item.name" ng-style="setdirectionClass(item.name)"> </span> <span ng-style="setdirectionClass(item.name)"> <span ng-class="setArrowClass(item.name)"></span> <span time-ago title="{{item.date}}"></span> <span> <p ng-bind-html="item.msg | smilies"></p> </span> </span> </li> </ul> </div> </div> </div> </div> </div> <div id="agentList" tabindex="-1" role="dialog" aria-labelledby="cmdLabel" aria-hidden="true"> <div> <div> <div> <div> <button type="button" data-dismiss="modal" aria-hidden="true">×</button> </div> <h2> <span></span>لیست تمام کارشناسان </h2> </div> <div> <div style="display: block;"> <div ng-show="agentList.length==0"> کارشناس آنلاینی وجود ندارد </div> <ul ng-repeat="item in agentList"> <li> <span> <a ng-click="engageVisitor(item.id)">{{item.name}}</a> </span> </li> </ul> </div> </div> </div> </div> </div> <div id="agentUserChat" tabindex="-1" role="dialog" aria-labelledby="cmdLabel" aria-hidden="true"> <div> <div> <div> <div> <button type="button" data-dismiss="modal" aria-hidden="true">×</button> </div> <h2> <span></span>گفتگو </h2> </div> <div> <div> <div> <div style="display: block;"> <label>شروع چت در </label>: <span ng-bind="dateStartChat"></span> <ul> <li ng-repeat="item in agentUserMsgs"> <span ng-bind="item.name" ng-style="setdirectionClass(item.name)"> </span> <span ng-style="setdirectionClass(item.name)"> <span ng-class="setArrowClass(item.name)"></span> <span time-ago title="{{item.date}}"></span> <span> <p ng-bind-html="item.msg | smilies"></p> </span> </span> </li> </ul> <div> <div> <textarea id="post-msg" ng-model="msg" placeholder="متن خود را وارد نمایید" style="overflow: hidden; word-wrap: break-word; resize: horizontal; height: 80px; max-width: 100%"></textarea> <span smilies-selector="msg" smilies-placement="right" smilies-title="Smilies"></span> </div> <div style="text-align: center; margin-top: 5px"> <button ng-click="agentMsgToUser(msg)">ارسال</button> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div>
فایل index.cshtml
<html ng-app="app"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Live Support</title> <link href="~/Content/bootstrap-rtl.css" rel="stylesheet" /> <link href="~/Scripts/smilies/angular-smilies-embed.css" rel="stylesheet" /> <link href="~/Content/font-awesome.css" rel="stylesheet" /> <link href="~/Content/toastr.css" rel="stylesheet" /> <link href="~/Content/liveSupport.css" rel="stylesheet" /> <script src="~/Scripts/jquery-1.10.2.js"></script> <script src="~/Scripts/toastr.js"></script> <script src="~/Scripts/jquery.timeago.js"></script> <script src="~/Scripts/angular.js"></script> <script src="~/Scripts/angular-animate.js"></script> <script src="~/Scripts/angular-sanitize.js"></script> <script src="~/Scripts/angular-route.js"></script> <script src="~/Scripts/angular-strap.js"></script> <script src="~/Scripts/angular-strap.tpl.js"></script> <script src="~/Scripts/smilies/angular-smilies.js"></script> <script src="~/Scripts/jquery.signalR-2.2.0.js"></script> <script src="~/Scripts/angular-signalr-hub.js"></script> <script src="~/app/app.js"></script> @Scripts.Render("~/bundles/bootstrap") </head> <body ng-controller="ChatCtrl"> <div ng-view> </div> <div id="chat-box-header" ng-click="boxheader()"> {{chatTitle}} </div> <div id="chat-box"> <div ng-show="hasOnline"> <div id="style-1" style="min-height:100px;"> <div ng-repeat="item in clientAgentMsg track by $index"> <span ng-class="setClass(item.name)"> {{item.name}} </span> <br /> <p ng-bind-html="item.msg | smilies"></p> </div> </div> <div> <label>پیام</label> <div style="text-align: left; clear: both"> <a data-placement="top" data-trigger="hover " bs-tooltip="options.alarm" ng-click="alarm()"><i></i></a> <a data-placement="top" data-trigger="hover " bs-tooltip="options.signOut" href="signOut()"><i></i><span></span></a> <a data-placement="top" data-trigger="hover " bs-tooltip="options.upload" href="fileupload()"> <span><i></i></span> </a> </div> <div> <textarea style="height: 150px; max-height: 160px;" ng-model="message" placeholder=" متن خود را وارد نمایید"></textarea> <span smilies-selector="message" smilies-placement="right" smilies-title="Smilies"></span> </div> </div> <div style="text-align: center"> <button type="button" ng-disabled="pendingRequestChat" ng-click="requestChat(message)">ارسال </button> </div> </div> <div ng-show="hasOffline"> <div> <form name="Ticket" id="form1"> <fieldset> <div> <label>نام</label> <input name="email" ng-model="ticket.name" > </div> <div> <label>ایمیل</label> <input name="email" ng-model="ticket.email" > </div> <div> <label>پیام</label> </div> <div> <textarea ng-model="ticket.comment" placeholder="متن خود را وارد نمایید"></textarea> <span smilies-selector="ticket.comment" smilies-placement="right" smilies-title="Smilies"></span> </div> </fieldset> <div style="text-align: center"> <button type="button" ng-click="ticket.submit(ticket)"> ارسال </button> </div> </form> </div> </div> </div> </body> </html>
نکات تکمیلی :
نگاشت Hubها به برنامه در مسیر ("signalr /") در فایل ConfigureAuth.Cs
app.MapSignalR();
یکی از ابزارهایی که برای این بسته تولید شدهاست و به شما کمک میکند تا بسته بندی راحتتری داشته باشید و یک نوع ویزارد را در CLI به شما نشان میدهد، ابزار electron-packager-interactive است که به طریق زیر آن را نصب میکنیم:
npm install -g electron-packager-interactive
electron-packager-interactive
epi
? Overwrite output directory ? Yes ? Use asar source packaging ? Yes ? Select Electron app source directory: E:\Projects\electron\dbtest ? Select Electron app output directory: E:\Projects\electron\dbtest\releases ? Select Application name: myapp ? Select App Bundle Id (optional): 12 ? Select App Version(optional): 0.0.1 ? Select Electron icon file: ? Select Electron version release: 0.34.1 ? Select platforms: all ? Select architecture: all
پیکربندی از طریق packages.json
شما میتوانید برای بسته بندی از طریق این فایل، یک پروفایل ایجاد کنید و بر اساس اطلاعات هر پروفایلی که دوست دارید، بسته بندی را انجام دهید؛ یا مثلا دوست دارید برای هر پلتفرم تنظیمات خاصی را داشته باشید. برای اینکار شما میتوانید به تعداد دلخواهی از خصوصیت build را مانند کد زیر داشته باشید:
"scripts": { "start": "./node_modules/.bin/electron .", "test":"eslint .", "build":"electron-packager . myapp --platform=all --arch=all --overwrite --ignore=node_modules/dev-dependency --asar", "build:win32":"electron-packager . myapp --platform=win32 --arch=ia32 --overwrite --ignore=node_modules/dev-dependency --asar", "build:win64":"electron-packager . myapp --platform=win64 --arch=x64 --overwrite" },
در کد بالا من سه نوع پیکربندی را برای build تعریف کردهام. در اولین پیکربندی بسته بندی برنامه برای همه پلتفرمها و معماریها انجام میگیرد و در دومین پیکربندی فقط برای سیستم عامل ویندوز نسخه 32 بیتی آن و در سومین پیکربندی، نسخه 64 بیتی مد نظر است که البته ویژگی asar و فلگ ignore را مانند دو پیکربندی قبلی به همراه ندارد. برای صدا زدن آنها در CLI هم باید به شکل زیر عمل کنید:
npm run build npm run build:win32 npm run build:win64
افزودن فایلهای ایستا به بسته بندی
یکی از مشکلاتی که هنگام بسته بندی به آن برخوردم این بود که فایل sql.txt به بسته نهایی اضافه نشده بود و برنامه به همین علت terminate میشد. برای اینکه به این مشکل دچار نشوید، بهتر هست این موارد را داخل یک دایرکتوری قرار داده تا بعد از بسته بندی، دایرکتوری را به راحتی به بسته نهایی کپی کنید. با توجه به صحبتهایی که با یکی از تولیدکنندگان پکیج داشتم، ایشان متذکر شدند با اینکه این بسته حاوی یک لیست سیاه برای انتقال فایلهاست، ولی فایلی مثل sql.txt مشکلی ندارد. اما با این حال اگر به مشکل برخورد کردید، این مورد را خودتان دستی انجام دهید.