2019年12月17日 星期二

格式化為金額字串且去除結尾多餘小數

$scope.不含結尾多餘小數金額 = function (value, 小數位) {
       var s = $filter('currency')(value, '', 小數位);
        if (s.indexOf('.') > 0 && s.right(1) == '0') s = trim(trim(s, '0'), '.');
        return s;
    }

function trim(s, c) {
    if (c === "]") c = "\\]";
    if (c === "\\") c = "\\\\";
    return s.replace(new RegExp(
        "^[" + c + "]+|[" + c + "]+$", "g"
    ), "");
}

String.prototype.right = function (num) {
    return this.substring(this.length - num, this.length);
}

2019年11月26日 星期二

2019年10月31日 星期四

於 controller 外取得 scope 物件

<div ng-app="module1" ng-controller="ctrl" id="divctrl">

var scope = angular.element(document.getElementById('divctrl')).scope();
scope.$apply(function () {
  scope.speakers = ['11','22'];
});

2019年10月30日 星期三

UI-Calendar 使用說明

安裝套件: fullcalendar, UI-Calendar

javascript
====
 <script type='text/javascript' src="Scripts/moment.min.js"></script>
    <script type='text/javascript' src="Scripts/fullcalendar/fullcalendar.min.js"></script>
    <script type='text/javascript' src="Scripts/fullcalendar/gcal.min.js"></script>
    <script type='text/javascript' src="Scripts/fullcalendar/locale/zh-tw.js"></script>
    <link href="content/fullcalendar.min.css" rel="stylesheet" type="text/css" />
    <script type='text/javascript' src="Scripts/calendar.js"></script>
angular.module('module1', ['ui.calendar', 'ui.bootstrap'])
            .controller("ctrl1", function ($scope, $http, $filter, $compile, $timeout, uiCalendarConfig) {
$scope.eventRender = function (event, element, view) {
                    element.attr({
                        'title': event.title,
                        'tooltip-append-to-body': true,
                    });
                    $compile(element)($scope);
                };
 $scope.getCalendar = function () {
                    if ($scope.eventSources.length > 0) $scope.eventSources.splice(0, $scope.eventSources.length);
                    $http.post('...', { from: $scope.from || null, to: $scope.to || null })
                        .then(function (result) {
                            $scope.eventSources.push(result.data);
                        });
                }
                $scope.uiConfig = {
                    calendar: {
                        displayEventTime: false,
                        eventRender: $scope.eventRender,
                        viewRender: function (view, element) {
                             if (!$scope.from || $scope.from.toLocaleString() != view.start.toLocaleString()) {
                    $scope.from = view.start;
                    $scope.to = view.end;
                    $scope.getCalendar();
                }
                        },
                        dayClick: function (date, allDay, jsEvent, view) {
                            $scope.date = new Date(date);
 $('td.fc-past,td.fc-future').css({ "color": "unset", "backgroundColor": "unset" });
                            $('td.fc-today').css({ "color": "unset", "backgroundColor": "#fcf8e3" });
                            var MyDateString = $scope.date.getFullYear() + '-'
                                + ('0' + ($scope.date.getMonth() + 1)).slice(-2)
                                + "-" + ('0' + $scope.date.getDate()).slice(-2);
                            $('[data-date=' + MyDateString + ']').css({ "color": "red", "backgroundColor": "yellow" });
                        },
                        eventClick: function (event) {
                        }
                    }
                };
                $scope.eventSources = [];

html
====
<div ui-calendar="uiConfig.calendar" class="calendar" ng-model="eventSources" calendar="myCalendar"></div>

c#
====
var list = db.table1.Where(a => ...).AsEnumerable().Select(a => new { title =a.title, start = a.date1, end = a.date2, backgroundColor = "orange" });

2019年10月23日 星期三

filter 不穩定問題

ng-repeat="obj in activitys | filter: {'地點':location.value}"
有時 location.value 改變不會發生作用
只能改變寫法

ng-repeat="obj in activityfilters()"
$scope.activityfilters = function () {
                    if (!$scope.location) return null;
                    return Enumerable.From($scope.activitys).Where('$.地點==' + $scope.location.value).ToArray();
                }

2019年9月25日 星期三

asp.net webmethod 使用session 做資料交換的問題

實作上跨 webmethod 常常不能取得 System.Web.HttpContext.Current.Session 內的資料
例如在 webmethod1 寫入然後在 webmethod2 讀取時會是null
改用 System.Web.HttpContext.Current.Cache 則ok

2019年9月24日 星期二

合併檔案(tif格式)

安裝套件: FreeSpire.Office

using Spire.Doc;
using Spire.Pdf;

string randomTempFileName = System.IO.Path.GetTempFileName();
Bitmap bmp = default, bmpclone = default;
                Graphics gr = default;
                try
                {
List<System.Drawing.Image> images=new List<System.Drawing.Image>();
                    foreach (var file in files)
                    {
                        var ext = System.IO.Path.GetExtension(file.name).ToLower();
                        byte[] bytes =Convert.FromBase64String(file.value.Split(',')[1]);
                        Document doc = default;
                        using (MemoryStream ms = new MemoryStream(bytes))
                        using (MemoryStream ms1 = new MemoryStream())
                            switch (ext)
                            {
                                case ".doc":
                                    doc = new Document(ms, Spire.Doc.FileFormat.Doc);
                                    doc.SaveToStream(ms1, Spire.Doc.FileFormat.PDF);
                                    images.AddRange(SaveAsImage(new PdfDocument(ms1)));
                                    break;
                                case ".docx":
                                    doc = new Document(ms, Spire.Doc.FileFormat.Docx2010);
                                    doc.SaveToStream(ms1, Spire.Doc.FileFormat.PDF);
                                    images.AddRange(SaveAsImage(new PdfDocument(ms1)));
                                    break;
                                case ".pdf":
                                    images.AddRange(SaveAsImage(new PdfDocument(ms)));
                                    break;
                                case ".png":
                                case ".jpg":                                 
                                    bmp = (Bitmap)Bitmap.FromStream(ms);
                                    bmpclone = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);  // 配合 PdfDocument 轉為 image 的 PixelFormat 轉換成相同的
                                    gr = Graphics.FromImage(bmpclone);
                                    gr.DrawImage(bmp, new Rectangle(0, 0, bmpclone.Width, bmpclone.Height));
                                    images.Add((Image)bmpclone);
                                    break;                             
                            }
                    }
                    JoinTiffImages(images.ToArray(), randomTempFileName, EncoderValue.CompressionLZW);
                }
                catch (Exception ex)
                {
                    return 回傳錯誤(ex);
                }
                finally
                {
                    if (bmp != default) bmp.Dispose();
                    if (bmpclone != default) bmpclone.Dispose();
                    if (gr != default) gr.Dispose();
                }
                Image[] SaveAsImage(PdfDocument document)
                {
                    if (document.Pages.Count > 3) throw new Exception("無法處理超過3頁的檔案");
                    Image[] images = new Image[document.Pages.Count];
                    for (int i = 0; i < document.Pages.Count; i++)
                        images[i] = document.SaveAsImage(i);
                    return images;
                }
                void JoinTiffImages(Image[] images, string outFile, EncoderValue compressEncoder)
                {
                    //use the save encoder
                    Encoder enc = Encoder.SaveFlag;
                    EncoderParameters ep = new EncoderParameters(2);
                    ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.MultiFrame);
                    ep.Param[1] = new EncoderParameter(Encoder.Compression, (long)compressEncoder);
                    Image pages = images[0];
                    int frame = 0;
                    ImageCodecInfo info = GetEncoderInfo("image/tiff");
                    foreach (Image img in images)
                    {
                        if (frame == 0)
                        {
                            pages = img;
                            //save the first frame
                            pages.Save(outFile, info, ep);
                        }
                        else
                        {
                            //save the intermediate frames
                            ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.FrameDimensionPage);
                            pages.SaveAdd(img, ep);
                        }
                        if (frame == images.Length - 1)
                        {
                            //flush and close.
                            ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.Flush);
                            pages.SaveAdd(ep);
                        }
                        frame++;
                    }
                    ImageCodecInfo GetEncoderInfo(string mimeType)
                    {
                        ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
                        for (int j = 0; j < encoders.Length; j++)
                        {
                            if (encoders[j].MimeType == mimeType)
                                return encoders[j];
                        }
                        throw new Exception(mimeType + " mime type not found in ImageCodecInfo");
                    }
                }

2019年9月17日 星期二

組合 image 陣列為 tif 檔案

List<System.Drawing.Image> images=new List<System.Drawing.Image>();
...
JoinTiffImages(images.ToArray(), 檔名, EncoderValue.CompressionLZW);

void JoinTiffImages(Image[] images, string outFile, EncoderValue compressEncoder)
            {
                //use the save encoder
                Encoder enc = Encoder.SaveFlag;
                EncoderParameters ep = new EncoderParameters(2);
                ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.MultiFrame);
                ep.Param[1] = new EncoderParameter(Encoder.Compression, (long)compressEncoder);
                Image pages = images[0];
                int frame = 0;
                ImageCodecInfo info = GetEncoderInfo("image/tiff");
                foreach (Image img in images)
                {
                    if (frame == 0)
                    {
                        pages = img;
                        //save the first frame
                        pages.Save(outFile, info, ep);
                    }

                    else
                    {
                        //save the intermediate frames
                        ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.FrameDimensionPage);

                        pages.SaveAdd(img, ep);
                    }
                    if (frame == images.Length - 1)
                    {
                        //flush and close.
                        ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.Flush);
                        pages.SaveAdd(ep);
                    }
                    frame++;
                }
ImageCodecInfo GetEncoderInfo(string mimeType)
            {
                ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
                for (int j = 0; j < encoders.Length; j++)
                {
                    if (encoders[j].MimeType == mimeType)
                        return encoders[j];
                }
                throw new Exception(mimeType + " mime type not found in ImageCodecInfo");
            }
            }

把程式加到開始畫面

產生捷徑並放到
C:\Users\登入帳號\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
再從開始選單找到此捷徑並按右鍵 > 釘選到開始

2019年8月16日 星期五

visual studio 啟動偵錯時不要開啟新的瀏覽器

工具 > 選項

專案和方案 > web專案 : 取消勾選 "在瀏覽器視窗關閉時..."

偵錯 > 一般 : 取消勾選 "為 asdp.net 啟用..."

2019年7月25日 星期四

取得網站應用程式根網址

<%
    string baseUrl = Request.Url.Scheme + "://" + Request.Url.Authority + Request.ApplicationPath.TrimEnd('/') + "/";
%>

用法:
<script type="text/jscript">
    var someurl = '<%=baseUrl%>subpath/somepage.aspx';
</script>

透過 javascript 但不使用 window.open 來開啟網址

目的是為了避免被瀏覽器阻擋

function openURL(url, target) {
    var link = document.createElement("a");
    link.href = url;
    link.target = target;
    document.body.appendChild(link);
    link.onclick = function () {
        requestAnimationFrame(function () {
            document.body.removeChild(link);
            delete link;
        })
    };
    link.click();
}

openURL('http://www.google.com', '_new');

2019年7月8日 星期一

使用ag-grid 確保多人作業環境畫面永遠顯示最新資料

$scope.onTimeout = function () {
  if (!$scope.new) // 處於某種狀態暫時不更新
  {
      $http.post('...')
          .then(function (result) {
              var oldlist = Enumerable.From($scope.gridOptions.api.getModel().rowsToDisplay).Select('$.data');
              var newlist = Enumerable.From(result.data);
              var 增加資料s = [];
              newlist.ForEach(function (a) {
                  if (!oldlist.Any('$.id==' + a.id)) 增加資料s.push(a);
              });
              $scope.gridOptions.api.updateRowData({ add: 增加資料s, addIndex: 0 });
              var 減少資料s = [];
              oldlist.ForEach(function (a) {
                  if (!newlist.Any('$.id==' + a.id)) 減少資料s.push(a);
              });
              $scope.gridOptions.api.updateRowData({ remove: 減少資料s });                    Enumerable.From($scope.gridOptions.api.getModel().rowsToDisplay).ForEach(function (node) {
                  var newdata = newlist.Where('$.id==' + node.data.id).SingleOrDefault();
                  if (newdata && JSON.stringify(node.data) != JSON.stringify(newdata)) {
                    node.setData(newdata);
                    $scope.gridOptions.api.redrawRows({ rowNodes: [node] });
                  }
              });
          }).finally(function (data) {
              $timeout($scope.onTimeout, 10000);
          });
  }
  else $timeout($scope.onTimeout, 10000);
}

$timeout($scope.onTimeout, 10000);

頁面內容不要cache 確保每次都抓最新資料

Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);  // HTTP 1.1.
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.

※未經驗證

2019年7月6日 星期六

修改 line 畫面尺寸超過200%

開啟 C:\Users\[使用者帳號]\AppData\Local\Line\Data\LINE.ini
找到 scaleRatio=? (2=200%,3=300%)
若無此設定則手動添加

2019年7月5日 星期五

webmethod 回傳錯誤

var httpContext=System.Web.HttpContext.Current;
httpContext.Response.Write(錯誤訊息);
httpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
httpContext.Response.Flush();
httpContext.Response.SuppressContent = true;
httpContext.ApplicationInstance.CompleteRequest();

※若為 async 函數,await 後就無法再取得 System.Web.HttpContext.Current,必須先用變數記錄起來,await 之後再填回
var httpcontext = HttpContext.Current;
await xxxAsync().ConfigureAwait(false);
HttpContext.Current = httpcontext;

2019年7月1日 星期一

加密 web.config 避免敏感資料外洩

例如針對區段 connectionStrings 進行加密

cd C:\Windows\Microsoft.Net\Framework\v4.0.30319
aspnet_regiis -pef "connectionStrings" "[web.config 所在路徑]"

若為 winform app 則須先修改成 web.config

※必須在執行應用程式所屬電腦上進行加密,否則會無法正確解密

主控台程式不顯示dos 視窗及 main 不要結束

不顯示dos 視窗

main 不要結束
====
最後呼叫  Console.ReadLine();
此時會等待使用者按下enter 才往下,因此不會直接結束程式
搭配"不顯示dos 視窗"就不會誤按enter 而結束程式

2019年6月27日 星期四

解決電腦無故自動喚醒

找出自動喚醒原因: powercfg /waketimers

例如出現以下訊息
原因: Windows 將執行 'NT TASK\Microsoft\Windows\UpdateOrchestrator\Universal Orchestrator Start' 排定要求喚醒電腦的工 作。

則必須進入工作排程器找出 "Universal Orchestrator Start" 並取消勾選 "喚醒電腦以執行此工作"
但預設會沒有權限修改
必須透過 psexec 啟動工作排程器才能修改
psexec下載位置: https://docs.microsoft.com/en-us/sysinternals/downloads/psexec
透過 psexec啟動工作排程器: psexec.exe -i -s %windir%\system32\mmc.exe /s taskschd.msc

可順便檢查還有哪些排程也會自動喚醒電腦,然後一併修改,可參考"下次執行時間"欄位尋找

ui-select 複選架構設定 title 於整個按鈕

無法直接於 ui-select-match 中取得 $item 物件,必須透過下一層來設定,若只在下一層設定 title,滑鼠必須停在文字上才會顯示

<ui-select-match><span ui-select-match-title="{{$item.text}}">...</span></ui-select-match>

.directive('uiSelectMatchTitle', function () {
        return {
            link: function ($scope, $element, attrs) {
                $($element).parent().parent().attr('title', attrs.uiSelectMatchTitle);
            }
        }
    })

2019年6月24日 星期一

ui-select 允許保存自行輸入的資料

透過 onblur 判斷是否有選取既有選項,若未選取則動態設定 ng-model

.directive('selectonblur', function () {
        return {
            require: 'uiSelect',
            link: function ($scope, $element, attrs, $select) {
                var searchInput = $element.querySelectorAll('input.ui-select-search');
                if (searchInput.length !== 1) throw Error("bla");
                searchInput.on('blur', function () {
                    if (!$select.ngModel.$viewValue)
                        $scope.$apply(function () {
                            var user = { name: $select.search };
                            $select.ngModel.$setViewValue(user);
                        });
                });               
            }
        }
    })

2019年6月10日 星期一

應用程式避免執行多個實體

若已執行則不再執行
private void Form1_Load(object sender, EventArgs e) {
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1) Application.Exit();
}

2019年5月17日 星期五

統一編號驗證

<input uniformvalidate ng-model='uniform'><span style='color:red' ng-show="form1.$error.uniform">*</span>

.directive("uniformvalidate", function () {
return {
            restrict: 'A',
            require: "ngModel",
            link: function (scope, element, attributes, ngModel) {
                ngModel.$validators.uniform = function (modelValue, viewValue) {
                    return 檢查統一編號(modelValue);
                }
            }
        };
    })

function 檢查統一編號(NO) {
    if (!NO) return true;
    var SUM = 0;
    if (NO.length != 8) {
        return false;
    }
    var 統編檢查陣列 = '12121241'.split('');
    var 統編字元陣列 = NO.split("");
    for (i = 0; i <= 7; i++) {
        if (NO.charCodeAt() < 48 || NO.charCodeAt() > 57) {
            return false;
        }
        SUM += 兩位數相加(統編字元陣列[i] * 統編檢查陣列[i]);
    }
    if (SUM % 10 == 0) return true;
    else if (統編字元陣列[6] == 7 && (SUM + 1) % 10 == 0) return true;
    else return false;
}

2019年5月9日 星期四

瀏覽器 console 出現錯誤訊息找不到 glyphicons-halflings-regular.woff2

訊息如下
.../fonts/glyphicons-halflings-regular.woff2 net::ERR_ABORTED 404 (Not Found)
.../fonts/glyphicons-halflings-regular.woff net::ERR_ABORTED 404 (Not Found)

Bootstrap 會使用到,需要把檔案複製到指定路徑,並修改 web.config 加入
<system.webServer>
    <staticContent>
      <remove fileExtension=".woff" />
      <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
      <remove fileExtension=".woff2" />
      <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
    </staticContent>

2019年4月15日 星期一

2019年3月18日 星期一

以純html+css模擬彈出式窗口

 <div style="box-shadow:4px 4px 3px rgba(20%,20%,40%,0.5);border-radius:10px; padding:10px; text-align:center;background-color:lemonchiffon;position:absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);z-index:9999;width:500px;" ng-show="showDownload">
            <span ng-click="showDownload=false" class='glyphicon glyphicon-remove' style="position:absolute;right:5px;top:5px;font-size:30px;"></span>
            <h3>請根據下載用途選擇</h3>
            <div class="btn-group" style="width:100%;"><button class="btn btn-primary" style="width:50%" ng-click="excel(1)">內部用</button><button class="btn btn-warning" style="width:50%" ng-click="excel(2)">供應商</button></div>
        </div>

※控制 showDownload 決定是否顯示窗口

2019年3月5日 星期二

ng-options 對應 ng-model 為物件時正確顯示預設值

使用 track by 指定物件對應屬性找出預設選項
ng-model="food.vendor" ng-options='a.text for a in food.vendors track by a.value'
=>food.vendor.value=a.value

2019年2月26日 星期二

javascript 日期相關函數應用

去掉時間
Date.prototype.date = function () {
   this.setHours(0, 0, 0, 0);
    return this;
}

月最後一天
Date.prototype.lastDay = function () {
    return new Date(this.getFullYear(), this.getMonth() + 1, 0);
}

月第一天
Date.prototype.firstDay = function () {
    return new Date(this.getFullYear(), this.getMonth(), 1);
}

增加月份
Date.prototype.addMonth = function (x) {
    this.setMonth(this.getMonth() + x);
    return this;
}

增加天數
Date.prototype.addDay = function (x) {
    this.setDate(this.getDate() + x);
    return this;
}

格式化為yyyymmdd
Date.prototype.yyyymmdd = function () {
    var mm = this.getMonth() + 1;
    var dd = this.getDate();
    return [this.getFullYear(),
    (mm > 9 ? '' : '0') + mm,
    (dd > 9 ? '' : '0') + dd
    ].join('');
};

2019年2月20日 星期三

uib-tabset 內使用 ng-model 方式

uib-tabset 使用自己的scope,若要用到原本的scope,必須綁定物件,不能綁定變數,也無法透過 $parent 綁定變數

$scope.store = { value: null };

<uib-tabset active="activeTab">
<uib-tab index="gridOptionsIn">
<select ng-model="store.value" ng-options="a.value as a.text for a in stores"></select>

ng-options 搭配 ng-model 注意

若 ng-model 綁定物件屬性,則選擇項目後會連動改到物件屬性 (但ng-model 還是會抓到正確的值),請不要再針對陣列尋找該屬性值對應物件,若要找出對應物件,則 ng-model 就綁定到物件,不要直接綁定物件屬性,或者增加一個屬性同樣對應到 ng-model 要綁定的屬性,以此屬性來查詢

例:
list=[{value:1,text:'a'},{value:2,text:'b'},{value:3,text:'c'},]
ng-options="a.value as a.text for a in list"
選擇第1個項目
ng-model=1
選擇第2個項目
ng-model=2
list=[{value:2,text:'a'},{value:2,text:'b'},{value:3,text:'c'},] (text:'a' 的 value 被改成 2)

增加另一個屬性對應 value
list=[{value:1,text:'a',id=1},{value:2,text:'b',id=2},{value:3,text:'c',id=3},]
以 id 來找物件

※不建議直接綁定物件屬性,曾經遇過該屬性會被莫名清空的問題,推測是因為 ng-options 的集合被清空造成連動,建議透過 ng-change 設定要綁定的物件屬性
ng-change="selectData['廠商編號']=selectData.vendor.value" ng-model="selectData.vendor" ng-options="a.text for a in selectData.vendors"

2019年2月15日 星期五

EF 匯入 view 注意 primary key 問題

自動變成PK : 使用 isnull 或 定義為 not null
不會變成PK : 使用 nullif

避免 view 的 not null 欄位且非PK 於EF 被誤判為PK
若不修正可能會造成儲存EF圖表時會刪除相關 .cs file

ex: select nullif(a.active,0) active,... from table1 a inner join table2 b on a.oid=b.id

2019年1月11日 星期五

Cannot read property '$$nextSibling' of null

緣由: 執行某個指令出現此錯誤訊息

解法:
$timeout(function () {
               // 造成錯誤的指令
}, 100);

2019年1月10日 星期四

透過工作排程器定時呼叫 url

程式 : powershell.exe (版本須為3以上)
引數 : -command "Invoke-WebRequest -URI \"http://...\" -Method Post"

2019年1月4日 星期五

input 連結 datalist 用程式控制彈出選項

範例: nextTick(() => document.querySelector('input').showPicker());  ※僅支援現代瀏覽器