加入 CodeStore VBA 模組
This commit is contained in:
1 parent
9c5364f88e
commit
0e638bdd15
22 files changed
+4510
No files matched your search
@@ -0,0 +1,198 @@
|
|||||||
|
Attribute VB_Name = "Module1"
|
||||||
|
'**************** MySQL變數 ******************
|
||||||
|
Const server_name = "ms1.tc-clothing.com" ' 伺服器IP,127.0.0.1為目前本機
|
||||||
|
Const database_name = "clothing" ' 連線資料庫名稱
|
||||||
|
Const user_id = "clothing" ' 資料庫登入帳號
|
||||||
|
Const UPassword = "clo256" ' 資料庫登入密碼
|
||||||
|
'********************************************
|
||||||
|
Public PYEAR As Integer
|
||||||
|
Public PCOMPANY As Integer
|
||||||
|
Public XLSfile As String
|
||||||
|
|
||||||
|
|
||||||
|
Public Function Cnmyopen() As ADODB.Connection
|
||||||
|
Dim conn As New ADODB.Connection
|
||||||
|
Dim connStr As String '資料庫連線字串,其中5.3為ODBC驅動程式版本,要配合你安裝的驅動。
|
||||||
|
connStr = "DRIVER={MariaDB ODBC 3.2 Driver}" _
|
||||||
|
& ";SERVER=" & server_name _
|
||||||
|
& ";DATABASE=" & database_name _
|
||||||
|
& ";UID=" & user_id _
|
||||||
|
& ";PWD=" & UPassword
|
||||||
|
conn.Open connStr
|
||||||
|
Set Cnmyopen = conn
|
||||||
|
' 傳回Connection
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Cnxnopen(strSQL As String, v_imex As String) As ADODB.Connection
|
||||||
|
' strSQL 寫入檔案 v_imex 1=唯讀 0=寫入
|
||||||
|
Dim conn As New ADODB.Connection
|
||||||
|
Dim connStr As String '資料庫連線字串
|
||||||
|
connStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & Excel.ThisWorkbook.path & _
|
||||||
|
strSQL & ";Extended Properties=""Excel 12.0;HDR=Yes;IMEX=" & v_imex & ";"";"
|
||||||
|
conn.Open connStr
|
||||||
|
Set Cnxnopen = conn
|
||||||
|
' 傳回Connection
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function rstCnxnopen(strXLSfile As String, strSQL As String) As ADODB.Recordset
|
||||||
|
' 開啟Excel表
|
||||||
|
Dim Cnxn As ADODB.Connection
|
||||||
|
Dim rstXLS As New ADODB.Recordset
|
||||||
|
Set Cnxn = Cnxnopen(strXLSfile, "1")
|
||||||
|
rstXLS.Open strSQL, Cnxn, adOpenStatic, adLockOptimistic, adCmdText
|
||||||
|
Set rstCnxnopen = rstXLS
|
||||||
|
' 傳回Recordset
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function rstCnmyopen(Cnmy As ADODB.Connection, strSQL As String) As ADODB.Recordset
|
||||||
|
' 開啟Mysql表
|
||||||
|
Dim rstSQL As ADODB.Recordset
|
||||||
|
Set rstSQL = New ADODB.Recordset
|
||||||
|
rstSQL.CursorLocation = 3 'mysql RecordCount連線BUG by 藍色小舖 老頑童
|
||||||
|
rstSQL.Open strSQL, Cnmy
|
||||||
|
Set rstCnmyopen = rstSQL
|
||||||
|
' 傳回Recordset
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function sqlCnxnexec(strXLSfile As String, strSQL As String)
|
||||||
|
' 執行SQL
|
||||||
|
Dim cn As New ADODB.Connection
|
||||||
|
' Dim Cnxn As ADODB.Connection
|
||||||
|
' Set Cnxn = Cnxnopen(strXLSfile, "1")
|
||||||
|
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
|
||||||
|
"Data Source=" & Excel.ThisWorkbook.path & strXLSfile & ";" & _
|
||||||
|
"Extended Properties=""Excel 8.0;HDR=YES;IMEX=0"""
|
||||||
|
cn.Execute strSQL
|
||||||
|
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Sub truncate_data(Cnmy As ADODB.Connection, SQLYear As Variant, SQLCompany As Variant)
|
||||||
|
' SQLYear 年度 SQLCompany 公司代碼
|
||||||
|
Dim strSQL As String
|
||||||
|
strSQL = "delete FROM CUSTOMER where id BETWEEN " & SQLYear & SQLCompany & "0000 AND " & SQLYear & SQLCompany & "9999;"
|
||||||
|
' debug.print strSQL
|
||||||
|
Cnmy.Execute strSQL '刪除名冊
|
||||||
|
strSQL = "delete FROM SELLUP where id BETWEEN " & SQLYear & SQLCompany & "0000 AND " & SQLYear & SQLCompany & "9999;"
|
||||||
|
' debug.print strSQL
|
||||||
|
Cnmy.Execute strSQL '刪除購買
|
||||||
|
' 清除舊資料
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub insert_data(Cnmy As ADODB.Connection, rstTBL As ADODB.Recordset, SQLYear As Variant, SQLCompany As Variant)
|
||||||
|
' 匯入名冊資料 量身尺寸
|
||||||
|
' rstTBL Excel檔案 SQLYear 年度 SQLCompany 公司
|
||||||
|
Dim sb As StringBuilder
|
||||||
|
Set sb = New StringBuilder
|
||||||
|
'====================================+
|
||||||
|
Dim p As Long '+
|
||||||
|
Dim diag As New ProgressDialogue '+
|
||||||
|
'====================================+
|
||||||
|
' 建立正規表示法變數
|
||||||
|
Dim Regex As New RegExp
|
||||||
|
'===================================================================================+
|
||||||
|
p = 0 '+
|
||||||
|
diag.Configure "更新資料時間", "Now wasting your time...", 0, rstTBL.RecordCount '+
|
||||||
|
diag.Show '+
|
||||||
|
'===================================================================================+
|
||||||
|
sb.Append "INSERT INTO CUSTOMER(id,regdate,company,department,seat,cus_no,cus_name,cus_sex,chest,waist,hip,leg,height,weight,trousers,tailor) VALUES "
|
||||||
|
While Not rstTBL.EOF
|
||||||
|
With rstTBL.Fields
|
||||||
|
If Not IsNull(.item("c_name")) Then
|
||||||
|
sb.Append "(" & .item("ID") & "," 'Field id
|
||||||
|
sb.Append "'" & .item("regdate") & "'," 'Field regdate
|
||||||
|
sb.Append "" & .item("company") & "," 'Field company
|
||||||
|
sb.Append "" & .item("depart") & "," 'Field department
|
||||||
|
sb.Append "" & .item("seat") & "," 'Field seat
|
||||||
|
sb.Append "'" & .item("cus_no") & "'," 'Field cus_no
|
||||||
|
' 設定匹配規則
|
||||||
|
Regex.Pattern = "[^\u4E00-\u9FA5\u3000-\u303F\uFF00-\uFFEF\u0000-\u007F\u201c-\u201d]"
|
||||||
|
If Regex.test(.item("c_name")) Then Debug.Print .item("ID")
|
||||||
|
sb.Append "'" & .item("c_name") & "'," 'Field cus_name
|
||||||
|
sb.Append "" & .item("sex") & "," 'Field cus_sex
|
||||||
|
sb.Append "" & .item("chest") & "," 'Field chest
|
||||||
|
sb.Append "" & .item("waist") & "," 'Field waist
|
||||||
|
sb.Append "" & .item("hip") & "," 'Field hip
|
||||||
|
sb.Append "" & .item("leg") & "," 'Field leg
|
||||||
|
sb.Append "" & .item("height") & "," 'Field height
|
||||||
|
sb.Append "" & .item("weight") & "," 'Field weight
|
||||||
|
sb.Append "" & .item("trousers") & "," 'Field trousers
|
||||||
|
sb.Append "" & .item("tailor") & ")" 'Field tailor
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
rstTBL.MoveNext
|
||||||
|
If Not rstTBL.EOF Then sb.Append ","
|
||||||
|
'================================================+
|
||||||
|
diag.SetValue p '+
|
||||||
|
diag.SetStatus "Now wasting your time... " & p '+
|
||||||
|
p = p + 1 '+
|
||||||
|
'================================================+
|
||||||
|
Wend
|
||||||
|
' strSQL = Left(strSQL, Len(strSQL) - 1)
|
||||||
|
' Debug.Print sb.tostring
|
||||||
|
Cnmy.Execute sb.ToString
|
||||||
|
'============+
|
||||||
|
diag.Hide '+
|
||||||
|
'============+
|
||||||
|
'更新服裝尺碼
|
||||||
|
' Application.Calculation = xlAutomatic ' 設置自動重算
|
||||||
|
' MsgBox "更新完成!"
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub insert_data2(Cnmy As ADODB.Connection, rstXLS As ADODB.Recordset, SQLYear As Variant, SQLCompany As Variant)
|
||||||
|
' 新增資料段2 服裝尺碼
|
||||||
|
Dim sb As StringBuilder
|
||||||
|
Set sb = New StringBuilder
|
||||||
|
Dim rstGoodsUse As New ADODB.Recordset
|
||||||
|
' Debug.Print rstXLS!itsz1,rstXLS!item1
|
||||||
|
strSQL = "Select pno,mtmodel,mtname,mtcolor,wtmodel,wtname,wtcolor,tomeasure" & _
|
||||||
|
" FROM GoodsUse" & _
|
||||||
|
" WHERE corp_use=" & SQLCompany & _
|
||||||
|
" and Depart_Use=(select department from CUSTOMER" & _
|
||||||
|
" where id BETWEEN " & SQLYear & SQLCompany & "0000 AND " & SQLYear & SQLCompany & "9999" & _
|
||||||
|
" limit 1)order by pno"
|
||||||
|
rstGoodsUse.CursorLocation = 3 'mysql RecordCount連線BUG by 藍色小舖 老頑童
|
||||||
|
rstGoodsUse.Open strSQL, Cnmy
|
||||||
|
' Debug.Print rstGoodsUse.RecordCount
|
||||||
|
ItCount = rstGoodsUse.RecordCount '處理項目數
|
||||||
|
sb.Append "insert into SELLUP(id"
|
||||||
|
For i = 1 To ItCount
|
||||||
|
sb.Append "," & "itsz" & i & ",item" & i
|
||||||
|
Next i
|
||||||
|
sb.Append ") VALUES "
|
||||||
|
'資料來源動態設定
|
||||||
|
While Not rstXLS.EOF
|
||||||
|
sb.Append "(" & rstXLS!ID & ","
|
||||||
|
rstGoodsUse.MoveFirst
|
||||||
|
i = 1
|
||||||
|
While Not rstGoodsUse.EOF
|
||||||
|
sb.Append "goodsid('" & rstXLS.Fields.item("itsz" & i) & "'," & IIf(rstXLS!sex, rstGoodsUse!mtmodel, rstGoodsUse!wtmodel) & ")," & _
|
||||||
|
rstXLS.Fields.item("item" & i)
|
||||||
|
i = i + 1
|
||||||
|
rstGoodsUse.MoveNext
|
||||||
|
If Not rstGoodsUse.EOF Then sb.Append ","
|
||||||
|
Wend
|
||||||
|
sb.Append ")"
|
||||||
|
rstXLS.MoveNext
|
||||||
|
If Not rstXLS.EOF Then sb.Append ","
|
||||||
|
Wend
|
||||||
|
sb.Append ";"
|
||||||
|
' Debug.Print sb.tostring
|
||||||
|
Cnmy.Execute sb.ToString
|
||||||
|
rstGoodsUse.Close
|
||||||
|
' 完成資料新增
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub XLS_init()
|
||||||
|
' debug.print Worksheets("售價").Cells(3, 11)
|
||||||
|
PYEAR = Worksheets("售價").Cells(3, 11)
|
||||||
|
' debug.print Worksheets("售價").Cells(5, 11)
|
||||||
|
PCOMPANY = Worksheets("售價").Cells(5, 11)
|
||||||
|
' debug.print Worksheets("售價").Cells(7, 11)
|
||||||
|
XLSfile = Worksheets("售價").Cells(7, 11)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
Attribute VB_Name = "MyRibbon"
|
||||||
|
|
||||||
|
'namespace=vba-files/ribbons
|
||||||
|
|
||||||
|
'/*
|
||||||
|
'[Ribbon Menu Action]
|
||||||
|
'Ribbon Buttom 1 example call code
|
||||||
|
'
|
||||||
|
'*/
|
||||||
|
Public Sub btn1(ByRef control As Office.IRibbonControl)
|
||||||
|
On Error GoTo ErrorHandler
|
||||||
|
|
||||||
|
Dim wb1 As Workbook, wb2 As Workbook
|
||||||
|
Dim ws1 As Worksheet, ws2 As Worksheet
|
||||||
|
Dim PYEAR As String, PCOMPANY As String, XLSfile As String
|
||||||
|
Dim TM As Double, xRow As Long
|
||||||
|
|
||||||
|
' 初始化變數
|
||||||
|
InitializeVariables PYEAR, PCOMPANY, XLSfile
|
||||||
|
|
||||||
|
' 打開工作簿
|
||||||
|
Set wb1 = OpenWorkbook(Excel.ThisWorkbook.path & "\..\..\制服明細.xlsx")
|
||||||
|
Set wb2 = OpenWorkbook(Excel.ThisWorkbook.path & XLSfile)
|
||||||
|
|
||||||
|
' 設置工作表
|
||||||
|
Set ws1 = wb1.Sheets("Sheet1")
|
||||||
|
Set ws2 = wb2.Sheets("名冊")
|
||||||
|
|
||||||
|
' 執行主要操作
|
||||||
|
TM = Timer
|
||||||
|
xRow = 10000
|
||||||
|
ProcessData ws1, ws2, xRow
|
||||||
|
|
||||||
|
' 顯示執行時間
|
||||||
|
ws2.Range("AL1").value = Timer - TM
|
||||||
|
|
||||||
|
' 關閉工作簿
|
||||||
|
wb1.Close SaveChanges:=False
|
||||||
|
wb2.Close SaveChanges:=True
|
||||||
|
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
ErrorHandler:
|
||||||
|
MsgBox "發生錯誤: " & Err.Description, vbCritical
|
||||||
|
On Error Resume Next
|
||||||
|
If Not wb1 Is Nothing Then wb1.Close SaveChanges:=False
|
||||||
|
If Not wb2 Is Nothing Then wb2.Close SaveChanges:=False
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub InitializeVariables(ByRef PYEAR As String, ByRef PCOMPANY As String, ByRef XLSfile As String)
|
||||||
|
With ThisWorkbook.Worksheets("售價")
|
||||||
|
PYEAR = .Cells(3, 11).value
|
||||||
|
PCOMPANY = .Cells(5, 11).value
|
||||||
|
XLSfile = .Cells(7, 11).value
|
||||||
|
End With
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Function OpenWorkbook(ByVal path As String) As Workbook
|
||||||
|
Set OpenWorkbook = Workbooks.Open(path)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Sub ProcessData(ByRef ws1 As Worksheet, ByRef ws2 As Worksheet, ByVal xRow As Long)
|
||||||
|
Dim arr As Variant, Brr As Variant, Crr As Variant
|
||||||
|
Dim xD As Object
|
||||||
|
Dim i As Long
|
||||||
|
|
||||||
|
' 清除舊數據
|
||||||
|
ws2.Columns("AK:AK").Clear
|
||||||
|
ws2.Range("AL1").value = ""
|
||||||
|
|
||||||
|
' 獲取數據
|
||||||
|
arr = ws2.Range("F1").Resize(xRow)
|
||||||
|
Brr = ws1.Range("A1:F1").Resize(xRow)
|
||||||
|
Crr = Range("idx班代碼").Resize(xRow)
|
||||||
|
|
||||||
|
' 創建字典並填充數據
|
||||||
|
Set xD = CreateObject("Scripting.Dictionary")
|
||||||
|
FillDictionary xD, Crr, Brr
|
||||||
|
|
||||||
|
' 更新 Arr 數據
|
||||||
|
For i = 1 To UBound(arr)
|
||||||
|
If xD.Exists(arr(i, 1)) Then
|
||||||
|
arr(i, 1) = xD(arr(i, 1))
|
||||||
|
End If
|
||||||
|
Next i
|
||||||
|
|
||||||
|
' 將結果寫回工作表
|
||||||
|
ws2.Range("AK1").Resize(xRow) = arr
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub FillDictionary(ByRef xD As Object, ByRef Crr As Variant, ByRef Brr As Variant)
|
||||||
|
Dim i As Long
|
||||||
|
|
||||||
|
For i = 1 To UBound(Crr)
|
||||||
|
If Not IsEmpty(Crr(i, 1)) Then xD(Crr(i, 1)) = Crr(i, 2)
|
||||||
|
Next i
|
||||||
|
|
||||||
|
For i = 1 To UBound(Brr)
|
||||||
|
If Not IsEmpty(Brr(i, 6)) Then xD(Brr(i, 6)) = Brr(i, 4)
|
||||||
|
Next i
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'/*
|
||||||
|
'[Ribbon Menu Action]
|
||||||
|
'Ribbon Buttom 2 example call code
|
||||||
|
'
|
||||||
|
'*/
|
||||||
|
Public Sub btn2(ByRef control As Office.IRibbonControl)
|
||||||
|
' PYEAR 年度 PCOMPANY 公司代碼 XLSfile 同步檔案
|
||||||
|
Dim Cnmy As ADODB.Connection
|
||||||
|
Dim rstXLS As New ADODB.Recordset
|
||||||
|
Dim strSQL As String
|
||||||
|
Set Cnmy = Cnmyopen()
|
||||||
|
PYEAR = Worksheets("售價").Cells(3, 11)
|
||||||
|
PCOMPANY = Worksheets("售價").Cells(5, 11)
|
||||||
|
XLSfile = Worksheets("售價").Cells(7, 11)
|
||||||
|
Call truncate_data(Cnmy, PYEAR, PCOMPANY)
|
||||||
|
strSQL = "Select " & PYEAR & PCOMPANY & " & Format([編號],'0000') AS ID,Format(date(), 'yyyy-mm-dd') as regdate, company,[編號] as studentid,[班ID] as depart,[座號] as seat,[姓名] as c_name, IIf([性別]='男','true','false') AS sex" _
|
||||||
|
& ",[胸] as chest, [腰] as waist,[臀] as hip, [長] as leg,[身] as height,[重] as weight, [褲長] as trousers" _
|
||||||
|
& ",[學號] as cus_no,IIf([選取]='a','true','false') as tailor" _
|
||||||
|
& " FROM [帳務$] order by [編號];"
|
||||||
|
' Debug.Print strSQL
|
||||||
|
Set rstXLS = rstCnxnopen(XLSfile, strSQL)
|
||||||
|
Call insert_data(Cnmy, rstXLS, PYEAR, PCOMPANY)
|
||||||
|
rstXLS.Close
|
||||||
|
strSQL = "Select " & PYEAR & PCOMPANY & " & Format([編號],'0000') AS ID,IIf([性別]='男',true,false) AS sex" & _
|
||||||
|
", [校短尺碼] as itsz1, [校短訂購] as item1, [校長尺碼] as itsz2, [校長訂購] as item2" & _
|
||||||
|
", [校夏尺碼] as itsz3, [校夏訂購] as item3, [校冬尺碼] as itsz4, [校冬訂購] as item4, [校裙尺碼] as itsz5, [校裙訂購] as item5" & _
|
||||||
|
", [背心尺碼] as itsz6, [背心訂購] as item6, 'A' as itsz7, [領帶訂購] as item7, 'A' as itsz8, [腰帶訂購] as item8" & _
|
||||||
|
", 'A' as itsz9, [書包訂購] as item9" & _
|
||||||
|
" FROM [帳務$];"
|
||||||
|
' Debug.Print strSQL
|
||||||
|
Set rstXLS = rstCnxnopen(XLSfile, strSQL)
|
||||||
|
Call insert_data2(Cnmy, rstXLS, PYEAR, PCOMPANY)
|
||||||
|
rstXLS.Close
|
||||||
|
Cnmy.Close
|
||||||
|
' 執行SQL上傳
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
'/*
|
||||||
|
'[Ribbon Menu Action]
|
||||||
|
'Ribbon Buttom 2 example call code
|
||||||
|
'
|
||||||
|
'*/
|
||||||
|
Public Sub btn3(ByRef control As Office.IRibbonControl)
|
||||||
|
|
||||||
|
PYEAR = Worksheets("售價").Cells(3, 11)
|
||||||
|
PCOMPANY = Worksheets("售價").Cells(5, 11)
|
||||||
|
XLSfile = Worksheets("售價").Cells(7, 11)
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
Attribute VB_Name = "ProgressDialogue"
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Dim Cancelled As Boolean, showTime As Boolean, showTimeLeft As Boolean
|
||||||
|
Dim startTime As Long
|
||||||
|
Dim BarMin As Long, BarMax As Long, BarVal As Long
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function GetTickCount Lib "Kernel32" () As Long
|
||||||
|
|
||||||
|
'Title will be the title of the dialogue.
|
||||||
|
'Status will be the label above the progress bar, and can be changed with SetStatus.
|
||||||
|
'Min is the progress bar minimum value, only set by calling configure.
|
||||||
|
'Max is the progress bar maximum value, only set by calling configure.
|
||||||
|
'CancelButtonText is the caption of the cancel button. If set to vbNullString, it is hidden.
|
||||||
|
'optShowTimeElapsed controls whether the progress bar computes and displays the time elapsed.
|
||||||
|
'optShowTimeRemaining controls whether the progress bar estimates and displays the time remaining.
|
||||||
|
'calling Configure sets the current value equal to Min.
|
||||||
|
'calling Configure resets the current run time.
|
||||||
|
Public Sub Configure(ByVal title As String, ByVal status As String, _
|
||||||
|
ByVal Min As Long, ByVal Max As Long, _
|
||||||
|
Optional ByVal CancelButtonText As String = "Cancel", _
|
||||||
|
Optional ByVal optShowTimeElapsed As Boolean = True, _
|
||||||
|
Optional ByVal optShowTimeRemaining As Boolean = True)
|
||||||
|
Me.Caption = title
|
||||||
|
lblStatus.Caption = status
|
||||||
|
BarMin = Min
|
||||||
|
BarMax = Max
|
||||||
|
BarVal = Min
|
||||||
|
CancelButton.Visible = Not CancelButtonText = vbNullString
|
||||||
|
CancelButton.Caption = CancelButtonText
|
||||||
|
startTime = GetTickCount
|
||||||
|
showTime = optShowTimeElapsed
|
||||||
|
showTimeLeft = optShowTimeRemaining
|
||||||
|
lblRunTime.Caption = ""
|
||||||
|
lblRemainingTime.Caption = ""
|
||||||
|
Cancelled = False
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Set the label text above the status bar
|
||||||
|
Public Sub SetStatus(ByVal status As String)
|
||||||
|
lblStatus.Caption = status
|
||||||
|
DoEvents
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Set the value of the status bar, a long which is snapped to a value between Min and Max
|
||||||
|
Public Sub SetValue(ByVal value As Long)
|
||||||
|
If value < BarMin Then value = BarMin
|
||||||
|
If value > BarMax Then value = BarMax
|
||||||
|
Dim progress As Double, runTime As Long
|
||||||
|
BarVal = value
|
||||||
|
progress = (BarVal - BarMin) / (BarMax - BarMin)
|
||||||
|
ProgressBar.Width = 292 * progress
|
||||||
|
lblPercent = Int(progress * 10000) / 100 & "%"
|
||||||
|
runTime = GetRunTime()
|
||||||
|
If showTime Then lblRunTime.Caption = "Time Elapsed: " & GetRunTimeString(runTime, True)
|
||||||
|
If showTimeLeft And progress > 0 Then _
|
||||||
|
lblRemainingTime.Caption = "Est. Time Left: " & GetRunTimeString(runTime * (1 - progress) / progress, False)
|
||||||
|
DoEvents
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Get the time (in milliseconds) since the progress bar "Configure" routine was last called
|
||||||
|
Public Function GetRunTime() As Long
|
||||||
|
GetRunTime = GetTickCount - startTime
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Get the time (in hours, minutes, seconds) since "Configure" was last called
|
||||||
|
Public Function GetFormattedRunTime() As String
|
||||||
|
GetFormattedRunTime = GetRunTimeString(GetTickCount - startTime)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Formats a time in milliseconds as hours, minutes, seconds.milliseconds
|
||||||
|
'Milliseconds are excluded if showMsecs is set to false
|
||||||
|
Private Function GetRunTimeString(ByVal runTime As Long, Optional ByVal showMsecs As Boolean = True) As String
|
||||||
|
Dim msecs&, hrs&, mins&, secs#
|
||||||
|
msecs = runTime
|
||||||
|
hrs = Int(msecs / 3600000)
|
||||||
|
mins = Int(msecs / 60000) - 60 * hrs
|
||||||
|
secs = msecs / 1000 - 60 * (mins + 60 * hrs)
|
||||||
|
GetRunTimeString = IIf(hrs > 0, hrs & " hours ", "") _
|
||||||
|
& IIf(mins > 0, mins & " minutes ", "") _
|
||||||
|
& IIf(secs > 0, IIf(showMsecs, secs, Int(secs + 0.5)) & " seconds", "")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Returns the current value of the progress bar
|
||||||
|
Public Function GetValue() As Long
|
||||||
|
GetValue = BarVal
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Returns whether or not the cancel button has been pressed.
|
||||||
|
'The ProgressDialogue must be polled regularily to detect whether cancel was pressed.
|
||||||
|
Public Function cancelIsPressed() As Boolean
|
||||||
|
cancelIsPressed = Cancelled
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Recalls that cancel was pressed so that they calling routine can be notified next time it asks.
|
||||||
|
Private Sub CancelButton_Click()
|
||||||
|
Cancelled = True
|
||||||
|
lblStatus.Caption = "Cancelled By User. Please Wait."
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
Attribute VB_Name = "StringBuilder"
|
||||||
|
Option Compare Text
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Dim MyBuffer() As String
|
||||||
|
Dim MyCurrentIndex As Long
|
||||||
|
Dim MyMaxIndex As Long
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
|
||||||
|
Clear
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub Clear()
|
||||||
|
MyCurrentIndex = 0
|
||||||
|
MyMaxIndex = 16
|
||||||
|
ReDim MyBuffer(1 To MyMaxIndex)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Appends the given Text to this StringBuilder
|
||||||
|
Public Sub Append(Text As String)
|
||||||
|
|
||||||
|
MyCurrentIndex = MyCurrentIndex + 1
|
||||||
|
|
||||||
|
If MyCurrentIndex > MyMaxIndex Then
|
||||||
|
MyMaxIndex = 2 * MyMaxIndex
|
||||||
|
ReDim Preserve MyBuffer(1 To MyMaxIndex)
|
||||||
|
End If
|
||||||
|
MyBuffer(MyCurrentIndex) = Text
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Returns the text in this StringBuilder
|
||||||
|
'Optional Parameter: Separator (default vbNullString) used in joining components
|
||||||
|
Public Function ToString(Optional Separator As String = vbNullString) As String
|
||||||
|
|
||||||
|
If MyCurrentIndex > 0 Then
|
||||||
|
ReDim Preserve MyBuffer(1 To MyCurrentIndex)
|
||||||
|
MyMaxIndex = MyCurrentIndex
|
||||||
|
ToString = Join(MyBuffer, Separator)
|
||||||
|
End If
|
||||||
|
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
Attribute VB_Name = "ThisWorkbook"
|
||||||
|
Private Sub Workbook_Open()
|
||||||
|
' 自動執行
|
||||||
|
Call XLS_init
|
||||||
|
Application.Caption = PYEAR & Worksheets("售價").Cells(3, 1)
|
||||||
|
End Sub
|
||||||
|
|
||||||
@@ -0,0 +1,334 @@
|
|||||||
|
Attribute VB_Name = "clsConcat"
|
||||||
|
Option Compare Binary
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
' String concatenation (joining strings together) can have a significant
|
||||||
|
' performance impact when you are using the ampersand character to join
|
||||||
|
' strings together. While negligible in occasional use, if you start
|
||||||
|
' running tens of thousands of these in a loop, it can really bog
|
||||||
|
' down the processing due to the memory reallocations happening behind
|
||||||
|
' the scenes. In those cases it is better to use the Mid$() function to
|
||||||
|
' change an existing buffer to build the return string.
|
||||||
|
|
||||||
|
' Special thanks to Nir Sofer - http://www.nirsoft.net/vb/strclass.html
|
||||||
|
' and Chris Lucas - http://www.planetsourcecode.com/vb/scripts/ShowCode.asp?txtCodeId=37141&lngWId=1
|
||||||
|
' for their inspiration with these concepts.
|
||||||
|
|
||||||
|
' Set this to any character or string to add after each
|
||||||
|
' call to `.Add()`. A common example would be vbCrLf.
|
||||||
|
Public AppendOnAdd As String
|
||||||
|
|
||||||
|
' Set up an array of pages to hold strings
|
||||||
|
Private astrPages() As String
|
||||||
|
Private lngCurrentPage As Long
|
||||||
|
Private lngCurrentPos As Long
|
||||||
|
Private lngPageSize As Long
|
||||||
|
Private lngInitialPages As Long
|
||||||
|
|
||||||
|
' These defaults can be tweaked as needed
|
||||||
|
Const clngPageSize As Long = 4096
|
||||||
|
Const clngInitialPages As Long = 100
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
' Prepares the initial buffer page
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
|
||||||
|
If lngPageSize = 0 Then lngPageSize = clngPageSize
|
||||||
|
If lngInitialPages = 0 Then lngInitialPages = clngInitialPages
|
||||||
|
|
||||||
|
' Set up the initial array of pages.
|
||||||
|
ReDim astrPages(0 To lngInitialPages - 1) As String
|
||||||
|
|
||||||
|
' Prepare first page
|
||||||
|
astrPages(0) = Space$(lngPageSize)
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
' Add 1 or more strings (avoiding the string conversion of paramarray)
|
||||||
|
Public Sub Add(str1 As String, Optional str2 As String, Optional str3 As String, Optional str4 As String, Optional str5 As String, _
|
||||||
|
Optional str6 As String, Optional str7 As String, Optional str8 As String, Optional str9 As String, Optional str10 As String)
|
||||||
|
If str1 <> vbNullString Then AddString str1
|
||||||
|
If str2 <> vbNullString Then AddString str2
|
||||||
|
If str3 <> vbNullString Then AddString str3
|
||||||
|
If str4 <> vbNullString Then AddString str4
|
||||||
|
If str5 <> vbNullString Then AddString str5
|
||||||
|
If str6 <> vbNullString Then AddString str6
|
||||||
|
If str7 <> vbNullString Then AddString str7
|
||||||
|
If str8 <> vbNullString Then AddString str8
|
||||||
|
If str9 <> vbNullString Then AddString str9
|
||||||
|
If str10 <> vbNullString Then AddString str10
|
||||||
|
AddString AppendOnAdd
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
' Add to the string buffer
|
||||||
|
Private Sub AddString(strAddString As String)
|
||||||
|
|
||||||
|
Dim lngLen As Long
|
||||||
|
Dim lngRemaining As Long
|
||||||
|
Dim lngAddStrPos As Long
|
||||||
|
Dim lngAddLen As Long
|
||||||
|
|
||||||
|
' Get length of new string
|
||||||
|
lngLen = Len(strAddString)
|
||||||
|
|
||||||
|
' No need to process a zero-length string
|
||||||
|
If lngLen > 0 Then
|
||||||
|
' Set starting position for string we are adding
|
||||||
|
lngAddStrPos = 1
|
||||||
|
|
||||||
|
' Continue filling pages till we reach the end of the new string
|
||||||
|
Do While lngAddStrPos <= lngLen
|
||||||
|
|
||||||
|
' Check to see if we need a new page
|
||||||
|
If lngCurrentPos = lngPageSize Then
|
||||||
|
' See if we already have a new page available in the array
|
||||||
|
If lngCurrentPage = UBound(astrPages) Then
|
||||||
|
' Need to add a page to the array.
|
||||||
|
ReDim Preserve astrPages(0 To lngCurrentPage + 1)
|
||||||
|
End If
|
||||||
|
' Prepare page as a buffer
|
||||||
|
lngCurrentPage = lngCurrentPage + 1
|
||||||
|
astrPages(lngCurrentPage) = Space$(lngPageSize)
|
||||||
|
lngCurrentPos = 0
|
||||||
|
End If
|
||||||
|
|
||||||
|
' See if it fits on the current page
|
||||||
|
lngRemaining = lngPageSize - lngCurrentPos
|
||||||
|
If (lngLen - (lngAddStrPos - 1)) <= lngRemaining Then
|
||||||
|
' Yes, add to current page.
|
||||||
|
lngAddLen = (lngLen - (lngAddStrPos - 1))
|
||||||
|
Mid$(astrPages(lngCurrentPage), lngCurrentPos + 1, lngAddLen) = Mid$(strAddString, lngAddStrPos)
|
||||||
|
lngAddStrPos = lngLen + 1
|
||||||
|
lngCurrentPos = lngCurrentPos + lngAddLen
|
||||||
|
Else
|
||||||
|
' Fill remaining available space on current page.
|
||||||
|
Mid$(astrPages(lngCurrentPage), lngCurrentPos + 1, lngRemaining) = Mid$(strAddString, lngAddStrPos, lngRemaining)
|
||||||
|
' Note position in new string
|
||||||
|
lngCurrentPos = lngPageSize
|
||||||
|
lngAddStrPos = lngAddStrPos + lngRemaining
|
||||||
|
End If
|
||||||
|
|
||||||
|
' Move to next page, if needed
|
||||||
|
Loop
|
||||||
|
|
||||||
|
End If
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
' Removes the specified number of chacters from the string.
|
||||||
|
' (Technically just moves the position back)
|
||||||
|
Public Sub Remove(lngChars As Long)
|
||||||
|
|
||||||
|
Dim lngTotalLen As Long
|
||||||
|
Dim lngNewPosition As Long
|
||||||
|
|
||||||
|
' Get total length of current string including all pages
|
||||||
|
lngTotalLen = lngCurrentPos + (lngCurrentPage * lngPageSize)
|
||||||
|
|
||||||
|
' We can't remove more characters than we put in the string to start with.
|
||||||
|
If lngChars > lngTotalLen Then
|
||||||
|
' Go to beginning
|
||||||
|
lngCurrentPage = 0
|
||||||
|
lngCurrentPos = 1
|
||||||
|
Else
|
||||||
|
' Get new absolute position
|
||||||
|
lngNewPosition = lngTotalLen - lngChars
|
||||||
|
' Calculate full pages
|
||||||
|
lngCurrentPage = (lngNewPosition \ lngPageSize)
|
||||||
|
' Set position on partial page
|
||||||
|
lngCurrentPos = lngNewPosition - (lngCurrentPage * lngPageSize)
|
||||||
|
End If
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
' Returns the accumulated string
|
||||||
|
Public Function GetStr() As String
|
||||||
|
|
||||||
|
Dim lngCnt As Long
|
||||||
|
|
||||||
|
' Prepare return string. This should be the filled pages plus the last
|
||||||
|
' partial page, divided by 2 to get the string length instead of byte length.
|
||||||
|
GetStr = Space$((lngCurrentPage * lngPageSize) + lngCurrentPos)
|
||||||
|
|
||||||
|
' Loop through filled pages, overlaying on return string.
|
||||||
|
' (Last partial page is automatically trimmed based on returned string size.)
|
||||||
|
' (If lngCurrentPos=0 then skip last page)
|
||||||
|
If Len(GetStr) > 0 Then
|
||||||
|
For lngCnt = 0 To lngCurrentPage - Abs(CBool(lngCurrentPos = 0))
|
||||||
|
Mid$(GetStr, (lngCnt * lngPageSize) + 1, lngPageSize) = astrPages(lngCnt)
|
||||||
|
Next lngCnt
|
||||||
|
End If
|
||||||
|
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
' Return a partial string from a specified position
|
||||||
|
Public Function MidStr(lngStart As Long, Optional lngLength As Long = -1) As String
|
||||||
|
|
||||||
|
Dim lngPage As Long
|
||||||
|
Dim lngPos As Long
|
||||||
|
Dim lngStartPage As Long
|
||||||
|
Dim lngStartPos As Long
|
||||||
|
|
||||||
|
' Prepare return string length.
|
||||||
|
If lngLength = -1 Then
|
||||||
|
' Return remaining string after lngStart
|
||||||
|
lngLength = (Length - lngStart) + 1
|
||||||
|
MidStr = Space$(lngLength)
|
||||||
|
Else
|
||||||
|
' Return a specified number of characters
|
||||||
|
MidStr = Space$(lngLength)
|
||||||
|
End If
|
||||||
|
|
||||||
|
' Determine start page and position for return string
|
||||||
|
lngStartPage = (lngStart - 1) \ lngPageSize ' Zero based page
|
||||||
|
lngStartPos = lngStart - (lngStartPage * lngPageSize)
|
||||||
|
|
||||||
|
' Loop through filled pages, overlaying on return string.
|
||||||
|
' (Last partial page is automatically trimmed based on returned string size.)
|
||||||
|
If Len(MidStr) > 0 Then
|
||||||
|
For lngPage = lngStartPage To lngCurrentPage
|
||||||
|
' Could start at any point on first page
|
||||||
|
If lngPage = lngStartPage Then
|
||||||
|
Mid$(MidStr, 1) = Mid$(astrPages(lngPage), lngStartPos)
|
||||||
|
' lngPos is the current position in the new string
|
||||||
|
lngPos = lngPageSize - (lngStartPos - 2)
|
||||||
|
Else
|
||||||
|
' Pull whole pages as needed
|
||||||
|
Mid$(MidStr, lngPos) = astrPages(lngPage)
|
||||||
|
lngPos = lngPos + lngPageSize
|
||||||
|
End If
|
||||||
|
' Exit when we have filled the requested string.
|
||||||
|
If lngPos > lngLength Then Exit For
|
||||||
|
Next lngPage
|
||||||
|
End If
|
||||||
|
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
'---------------------------------------------------------------------------------------
|
||||||
|
' Procedure : RTrim
|
||||||
|
' Author : Adam Waller
|
||||||
|
' Date : 8/14/2023
|
||||||
|
' Purpose : Trim trailing whitespace from content
|
||||||
|
'---------------------------------------------------------------------------------------
|
||||||
|
'
|
||||||
|
Public Function RTrim(Optional strTrimChars As String = " ")
|
||||||
|
Do
|
||||||
|
If Length < Len(strTrimChars) Then Exit Do
|
||||||
|
If RightStr(Len(strTrimChars)) = strTrimChars Then
|
||||||
|
Remove Len(strTrimChars)
|
||||||
|
Else
|
||||||
|
Exit Do
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
'---------------------------------------------------------------------------------------
|
||||||
|
' Procedure : Right
|
||||||
|
' Author : Adam Waller
|
||||||
|
' Date : 11/5/2020
|
||||||
|
' Purpose : Return the rightmost specified number of characters.
|
||||||
|
'---------------------------------------------------------------------------------------
|
||||||
|
'
|
||||||
|
Public Function RightStr(lngLength As Long) As String
|
||||||
|
If Length > lngLength Then
|
||||||
|
RightStr = MidStr((Length - lngLength) + 1)
|
||||||
|
Else
|
||||||
|
RightStr = GetStr
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
' returns the length of the string, based on the current position
|
||||||
|
' (Faster than building the string just to check the length)
|
||||||
|
Public Function Length() As Double
|
||||||
|
Length = (lngCurrentPage * lngPageSize) + lngCurrentPos
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
' Reset the buffer without changing the page size
|
||||||
|
Public Sub Clear()
|
||||||
|
|
||||||
|
Class_Initialize
|
||||||
|
|
||||||
|
' Reset positions
|
||||||
|
lngCurrentPage = 0
|
||||||
|
lngCurrentPos = 0
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
' Manually set page size if you want something different from the default.
|
||||||
|
Public Sub SetPageSize(lngNewPageSize As Long, Optional lngNewInitialPages As Long)
|
||||||
|
If lngCurrentPage > 0 Or lngCurrentPos > 1 Then
|
||||||
|
MsgBox "Please set the page size before adding any data", vbExclamation, "Error in clsConcat"
|
||||||
|
Else
|
||||||
|
lngPageSize = lngNewPageSize
|
||||||
|
If lngNewInitialPages > 0 Then lngInitialPages = lngNewInitialPages
|
||||||
|
' Reinitialize with the updated sizes
|
||||||
|
Class_Initialize
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
' Test the class to make sure we are paging correctly.
|
||||||
|
Public Sub SelfTest()
|
||||||
|
|
||||||
|
SetPageSize 10, 5
|
||||||
|
|
||||||
|
Debug.Assert UBound(astrPages) = 4
|
||||||
|
Add "abcdefghij"
|
||||||
|
Add "k"
|
||||||
|
Debug.Assert Len(GetStr) = 11
|
||||||
|
Debug.Assert Length = 11
|
||||||
|
Remove 2
|
||||||
|
Debug.Assert Len(GetStr) = 9
|
||||||
|
Add "jkl"
|
||||||
|
Debug.Assert Len(GetStr) = 12
|
||||||
|
Debug.Assert GetStr = "abcdefghijkl"
|
||||||
|
Add "m123456789"
|
||||||
|
Remove 11
|
||||||
|
Debug.Assert GetStr = "abcdefghijk"
|
||||||
|
Debug.Assert MidStr(1, 1) = "a"
|
||||||
|
Debug.Assert MidStr(11, 1) = "k"
|
||||||
|
Debug.Assert MidStr(2, 3) = "bcd"
|
||||||
|
Debug.Assert MidStr(8) = "hijk"
|
||||||
|
Debug.Assert MidStr(10, 1) = "j"
|
||||||
|
Debug.Assert RightStr(1) = "k"
|
||||||
|
Debug.Assert RightStr(100) = "abcdefghijk"
|
||||||
|
|
||||||
|
' Verify paging
|
||||||
|
Clear
|
||||||
|
SetPageSize 5, 2
|
||||||
|
Add "1234"
|
||||||
|
Debug.Assert GetStr = "1234"
|
||||||
|
Add "5"
|
||||||
|
Debug.Assert GetStr = "12345"
|
||||||
|
Add "6"
|
||||||
|
Debug.Assert GetStr = "123456"
|
||||||
|
Add "789"
|
||||||
|
Debug.Assert GetStr = "123456789"
|
||||||
|
Add "0"
|
||||||
|
Debug.Assert GetStr = "1234567890"
|
||||||
|
Add "A"
|
||||||
|
Debug.Assert GetStr = "1234567890A"
|
||||||
|
Remove 1
|
||||||
|
Debug.Assert GetStr = "1234567890"
|
||||||
|
Remove 1
|
||||||
|
Debug.Assert GetStr = "123456789"
|
||||||
|
Add "0A"
|
||||||
|
Debug.Assert GetStr = "1234567890A"
|
||||||
|
Remove 2
|
||||||
|
Debug.Assert GetStr = "123456789"
|
||||||
|
Add "0A"
|
||||||
|
Debug.Assert GetStr = "1234567890A"
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,870 @@
|
|||||||
|
Attribute VB_Name = "stdArray"
|
||||||
|
'@TODO:
|
||||||
|
'* Implement Exceptions throughout all Array functions.
|
||||||
|
'* Fully implement pInitialised where necessary.
|
||||||
|
'* Build Methods Slice; Splice; Sort
|
||||||
|
'* Add methods from ruby
|
||||||
|
'* Documentation of methods
|
||||||
|
|
||||||
|
#If VBA6 Then
|
||||||
|
Private Declare PtrSafe Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As Long)
|
||||||
|
#Else
|
||||||
|
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)
|
||||||
|
#End If
|
||||||
|
Private Enum SortDirection
|
||||||
|
Ascending = 1
|
||||||
|
Descending = 2
|
||||||
|
End Enum
|
||||||
|
Private Type SortStruct
|
||||||
|
value As Variant
|
||||||
|
sortValue As Variant
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Private pArr() As Variant
|
||||||
|
Private pProxyLength As Long
|
||||||
|
Private pLength As Long
|
||||||
|
|
||||||
|
Private pChunking As Long
|
||||||
|
Private pInitialised As Boolean
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Public Event BeforeArrLet(ByRef arr As stdArray, ByRef arr As Variant)
|
||||||
|
Public Event AfterArrLet(ByRef arr As stdArray, ByRef arr As Variant)
|
||||||
|
Public Event BeforeAdd(ByRef arr As stdArray, ByVal iIndex As Long, ByRef item As Variant, ByRef cancel As Boolean)
|
||||||
|
Public Event AfterAdd(ByRef arr As stdArray, ByVal iIndex As Long, ByRef item As Variant)
|
||||||
|
Public Event BeforeRemove(ByRef arr As stdArray, ByVal iIndex As Long, ByRef item As Variant, ByRef cancel As Boolean)
|
||||||
|
Public Event AfterRemove(ByRef arr As stdArray, ByVal iIndex As Long)
|
||||||
|
Public Event AfterClone(ByRef Clone As stdArray)
|
||||||
|
Public Event AfterCreate(ByRef arr As stdArray)
|
||||||
|
|
||||||
|
'Create a stdArray object from params
|
||||||
|
'@param {paramarray variant()} The items of the array
|
||||||
|
'@returns {stdArray<variant>} A stdArray from the parameters.
|
||||||
|
Public Function Create(ParamArray params() As Variant) As stdArray
|
||||||
|
Set Create = New stdArray
|
||||||
|
|
||||||
|
Dim i As Long
|
||||||
|
Dim lb As Long: lb = LBound(params)
|
||||||
|
Dim ub As Long: ub = UBound(params)
|
||||||
|
|
||||||
|
Call Create.Init(ub - lb + 1, 10)
|
||||||
|
|
||||||
|
For i = lb To ub
|
||||||
|
Call Create.Push(params(i))
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Raise AfterCreate event
|
||||||
|
RaiseEvent AfterCreate(Create)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Create a stdArray object from params
|
||||||
|
'@param {Long} The length of the initial private array created
|
||||||
|
'@param {Long} The number of items the private array is increased by when required.
|
||||||
|
'@param {paramarray variant()} The items of the array
|
||||||
|
'@returns {stdArray<variant>} A stdArray from the parameters.
|
||||||
|
Public Function CreateWithOptions(ByVal iInitialLength As Long, ByVal iChunking As Long, ParamArray params() As Variant) As stdArray
|
||||||
|
Set CreateWithOptions = New stdArray
|
||||||
|
|
||||||
|
Dim i As Long
|
||||||
|
Dim lb As Long: lb = LBound(params)
|
||||||
|
Dim ub As Long: ub = UBound(params)
|
||||||
|
|
||||||
|
Call CreateWithOptions.Init(iInitialLength, iChunking)
|
||||||
|
For i = lb To ub
|
||||||
|
Call CreateWithOptions.Push(params(i))
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Raise AfterCreate event
|
||||||
|
RaiseEvent AfterCreate(Create)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Create a stdArray object from a VBA array
|
||||||
|
'@param {variant()} Variant array to create a `stdArray` object from.
|
||||||
|
'@returns {stdArray<variant>} Returns `stdArray` of variants.
|
||||||
|
Public Function CreateFromArray(ByVal arr As Variant) As stdArray
|
||||||
|
Set CreateFromArray = New stdArray
|
||||||
|
|
||||||
|
Dim i As Long
|
||||||
|
Dim lb As Long: lb = LBound(arr)
|
||||||
|
Dim ub As Long: ub = UBound(arr)
|
||||||
|
Call CreateFromArray.Init(ub - lb + 1, 10)
|
||||||
|
|
||||||
|
For i = lb To ub
|
||||||
|
Call CreateFromArray.Push(arr(i))
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Raise AfterCreate event
|
||||||
|
RaiseEvent AfterCreate(Create)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Create an array by splitting a string
|
||||||
|
'@param {string} Haystack to split
|
||||||
|
'@param {string?=","} Delimiter
|
||||||
|
'@returns {stdArray<string>} A list of strings
|
||||||
|
Public Function CreateFromString(ByVal sHaystack As String, Optional ByVal sDelimiter As String = ",") As stdArray
|
||||||
|
Set CreateFromString = CreateFromArray(Split(sHaystack, sDelimiter))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Initialise array
|
||||||
|
'@param {Long} The length of the initial private array created
|
||||||
|
'@param {Long} The number of items the private array is increased by when required.
|
||||||
|
Friend Sub Init(ByVal iInitialLength As Long, ByVal iChunking As Long)
|
||||||
|
If iChunking > iInitialLength Then iInitialLength = iChunking
|
||||||
|
If Not pInitialised Then
|
||||||
|
pProxyLength = iInitialLength
|
||||||
|
ReDim pArr(1 To iInitialLength) As Variant
|
||||||
|
pChunking = iChunking
|
||||||
|
pInitialised = True
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Obtain a collection from the data contained within the array. Primarily used for NewEnum() method.
|
||||||
|
'@returns {Collection} Collection from Array
|
||||||
|
Public Function AsCollection() As collection
|
||||||
|
Set AsCollection = New collection
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To Length()
|
||||||
|
AsCollection.Add pArr(i)
|
||||||
|
Next
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'For-each compatibility
|
||||||
|
'@protected
|
||||||
|
'@returns {IEnumVARIANT} An enumerator with methods enumNext, enumRefresh etc.
|
||||||
|
'@usage `For each obj in myEnum: ... : next`
|
||||||
|
'@TODO: Use custom IEnumVARIANT instead of casting to Collection
|
||||||
|
Public Property Get NewEnum() As IUnknown
|
||||||
|
Static oEnumCol As collection: If oEnumCol Is Nothing Then Set oEnumCol = AsCollection()
|
||||||
|
Set NewEnum = oEnumCol.[_NewEnum]
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'Obtain the length of the array
|
||||||
|
Public Property Get Length() As Long
|
||||||
|
Length = pLength
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'Obtain the length of the private array which stores the data of this array class
|
||||||
|
Public Property Get zProxyLength() As Long
|
||||||
|
zProxyLength = pProxyLength
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'Resize the array to a length
|
||||||
|
'@param {Long} The length of the desired array
|
||||||
|
Public Sub Resize(ByVal iLength As Long)
|
||||||
|
pLength = iLength
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Rechunk the private array to the length / number of items.
|
||||||
|
Public Sub Rechunk()
|
||||||
|
Dim fNumChunks As Double, iNumChunks As Long
|
||||||
|
fNumChunks = pLength / pChunking
|
||||||
|
iNumChunks = CLng(fNumChunks)
|
||||||
|
If fNumChunks > iNumChunks Then iNumChunks = iNumChunks + 1
|
||||||
|
|
||||||
|
ReDim Preserve pArr(1 To iNumChunks * pChunking) As Variant
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
'Sort the array
|
||||||
|
'@param {stdICallable<(variant)=>variant>} A mapping function which should map whatever the input is to whatever variant the array should be sorted on.
|
||||||
|
'@param {stdICallable<(variant,variant)=>boolean>} Comparrison function which consumes 2 variants and generates a boolean. See implementation of `Sort_QuickSort` for details.
|
||||||
|
'@param {long} Currently only 1 algorithm: 0 - Quicksort
|
||||||
|
'@param {boolean} Sort the array in place. Sorting in-place is prefferred if possible as it is much more performant.
|
||||||
|
'@returns {stdArray<T>}
|
||||||
|
Public Function Sort(Optional ByVal cbSortBy As stdICallable = Nothing, Optional ByVal cbComparrason As stdICallable = Nothing, Optional ByVal iAlgorithm As Long = 0, Optional ByVal bSortInPlace As Boolean = False) As stdArray
|
||||||
|
If Not bSortInPlace Then
|
||||||
|
Set Sort = Clone().Sort(cbSortBy, cbComparrason, iAlgorithm, True)
|
||||||
|
Else
|
||||||
|
If Length() = 0 Then
|
||||||
|
Set Sort = Me
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim arr() As SortStruct
|
||||||
|
ReDim arr(1 To Length()) As SortStruct
|
||||||
|
|
||||||
|
Dim i As Long
|
||||||
|
|
||||||
|
'Copy array to sort structures
|
||||||
|
For i = 1 To Length()
|
||||||
|
Call CopyVariant(arr(i).value, pArr(i))
|
||||||
|
If cbSortBy Is Nothing Then
|
||||||
|
Call CopyVariant(arr(i).sortValue, pArr(i))
|
||||||
|
Else
|
||||||
|
Call CopyVariant(arr(i).sortValue, cbSortBy.Run(pArr(i)))
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Call sort algorithm
|
||||||
|
Select Case iAlgorithm
|
||||||
|
Case 0 'QuickSort
|
||||||
|
Call Sort_QuickSort(arr, cbComparrason)
|
||||||
|
Case Else
|
||||||
|
stdError.Raise "Invalid sorting algorithm specified"
|
||||||
|
End Select
|
||||||
|
|
||||||
|
'Copy sort structures to array
|
||||||
|
For i = 1 To Length()
|
||||||
|
Call CopyVariant(pArr(i), arr(i).value)
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Return array
|
||||||
|
Set Sort = Me
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'QuickSort3
|
||||||
|
' Src: https://www.vbforums.com/showthread.php?473677-VB6-Sorting-algorithms-%28sort-array-sorting-arrays%29
|
||||||
|
' Omit plngLeft & plngRight; they are used internally during recursion
|
||||||
|
Private Sub Sort_QuickSort(ByRef pvarArray() As SortStruct, Optional cbComparrison As stdICallable = Nothing, Optional ByVal plngLeft As Long, Optional ByVal plngRight As Long)
|
||||||
|
Dim lngFirst As Long
|
||||||
|
Dim lngLast As Long
|
||||||
|
Dim varMid As SortStruct
|
||||||
|
Dim varSwap As SortStruct
|
||||||
|
|
||||||
|
If plngRight = 0 Then
|
||||||
|
plngLeft = 1
|
||||||
|
plngRight = Length()
|
||||||
|
End If
|
||||||
|
lngFirst = plngLeft
|
||||||
|
lngLast = plngRight
|
||||||
|
varMid = pvarArray((plngLeft + plngRight) \ 2)
|
||||||
|
Do
|
||||||
|
If cbComparrison Is Nothing Then
|
||||||
|
Do While pvarArray(lngFirst).sortValue < varMid.sortValue And lngFirst < plngRight
|
||||||
|
lngFirst = lngFirst + 1
|
||||||
|
Loop
|
||||||
|
Do While varMid.sortValue < pvarArray(lngLast).sortValue And lngLast > plngLeft
|
||||||
|
lngLast = lngLast - 1
|
||||||
|
Loop
|
||||||
|
Else
|
||||||
|
Do While cbComparrison.Run(pvarArray(lngFirst).sortValue, varMid.sortValue) And lngFirst < plngRight
|
||||||
|
lngFirst = lngFirst + 1
|
||||||
|
Loop
|
||||||
|
Do While cbComparrison.Run(varMid.sortValue, pvarArray(lngLast).sortValue) And lngLast > plngLeft
|
||||||
|
lngLast = lngLast - 1
|
||||||
|
Loop
|
||||||
|
End If
|
||||||
|
|
||||||
|
If lngFirst <= lngLast Then
|
||||||
|
varSwap = pvarArray(lngFirst)
|
||||||
|
pvarArray(lngFirst) = pvarArray(lngLast)
|
||||||
|
pvarArray(lngLast) = varSwap
|
||||||
|
lngFirst = lngFirst + 1
|
||||||
|
lngLast = lngLast - 1
|
||||||
|
End If
|
||||||
|
Loop Until lngFirst > lngLast
|
||||||
|
If plngLeft < lngLast Then Sort_QuickSort pvarArray, cbComparrison, plngLeft, lngLast
|
||||||
|
If lngFirst < plngRight Then Sort_QuickSort pvarArray, cbComparrison, lngFirst, plngRight
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'Obtain the array as a regular VBA array
|
||||||
|
Public Property Get arr() As Variant
|
||||||
|
If pLength = 0 Then
|
||||||
|
arr = Array()
|
||||||
|
Else
|
||||||
|
Dim vRet() As Variant
|
||||||
|
ReDim vRet(1 To pLength) As Variant
|
||||||
|
For i = 1 To pLength
|
||||||
|
Call CopyVariant(vRet(i), pArr(i))
|
||||||
|
Next
|
||||||
|
arr = vRet
|
||||||
|
End If
|
||||||
|
End Property
|
||||||
|
Public Property Let arr(v As Variant)
|
||||||
|
RaiseEvent BeforeArrLet(Me, v)
|
||||||
|
Dim lb As Long: lb = LBound(v)
|
||||||
|
Dim ub As Long: ub = UBound(v)
|
||||||
|
Dim cnt As Long: cnt = ub - lb + 1
|
||||||
|
ReDim pArr(1 To (Int(cnt / pChunking) + 1) * pChunking) As Variant
|
||||||
|
For i = lb To ub
|
||||||
|
Call Push(pArr(i))
|
||||||
|
Next
|
||||||
|
RaiseEvent AfterArrLet(Me, v)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'Add an element to the end of the array
|
||||||
|
'@param {variant} The element to add to the end of the array.
|
||||||
|
'@returns {stdArray} me
|
||||||
|
'TODO: Add multiple elements with push
|
||||||
|
Public Function Push(ByVal el As Variant) As stdArray
|
||||||
|
If pInitialised Then
|
||||||
|
'Before Add event
|
||||||
|
Dim bCancel As Boolean
|
||||||
|
RaiseEvent BeforeAdd(Me, pLength + 1, el, bCancel)
|
||||||
|
If bCancel Then Exit Function
|
||||||
|
|
||||||
|
If pLength = pProxyLength Then
|
||||||
|
pProxyLength = pProxyLength + pChunking
|
||||||
|
ReDim Preserve pArr(1 To pProxyLength) As Variant
|
||||||
|
End If
|
||||||
|
|
||||||
|
pLength = pLength + 1
|
||||||
|
CopyVariant pArr(pLength), el
|
||||||
|
|
||||||
|
'After add event
|
||||||
|
RaiseEvent AfterAdd(Me, pLength, pArr(pLength))
|
||||||
|
|
||||||
|
Set Push = Me
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Remove an element from the end of the array
|
||||||
|
'@returns {variant} The element removed from the array
|
||||||
|
Public Function Pop() As Variant
|
||||||
|
If pInitialised Then
|
||||||
|
If pLength > 0 Then
|
||||||
|
'Raise BeforeRemove event and optionally cancel
|
||||||
|
Dim bCancel As Boolean
|
||||||
|
RaiseEvent BeforeRemove(Me, pLength, pArr(pLength), bCancel)
|
||||||
|
If bCancel Then Exit Function
|
||||||
|
|
||||||
|
CopyVariant Pop, pArr(pLength)
|
||||||
|
pLength = pLength - 1
|
||||||
|
|
||||||
|
'Raise AfterRemove event
|
||||||
|
RaiseEvent AfterRemove(Me, pLength)
|
||||||
|
Else
|
||||||
|
Pop = Empty
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Remove the ith element from the array
|
||||||
|
'@param {Long} Index of the element to remove
|
||||||
|
'@returns {Variant} The element removed
|
||||||
|
Public Function Remove(ByVal index As Long) As Variant
|
||||||
|
'Ensure initialised
|
||||||
|
If pInitialised Then
|
||||||
|
'Ensure length > 0
|
||||||
|
If pLength > 0 Then
|
||||||
|
'Ensure index < length
|
||||||
|
If index <= pLength Then
|
||||||
|
'Raise BeforeRemove event and optionally cancel
|
||||||
|
Dim bCancel As Boolean
|
||||||
|
RaiseEvent BeforeRemove(Me, index, pArr(index), bCancel)
|
||||||
|
If bCancel Then Exit Function
|
||||||
|
|
||||||
|
'Copy party we are removing to return variable
|
||||||
|
CopyVariant Remove, pArr(index)
|
||||||
|
|
||||||
|
'Loop through array from removal, set i-1th element to ith element
|
||||||
|
Dim i As Long
|
||||||
|
For i = index + 1 To pLength
|
||||||
|
pArr(i - 1) = pArr(i)
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Set last element length and subtract total length by 1
|
||||||
|
pArr(pLength) = Empty
|
||||||
|
pLength = pLength - 1
|
||||||
|
|
||||||
|
'Raise after remove event
|
||||||
|
RaiseEvent AfterRemove(Me, index)
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Remove and return element from the start of the array
|
||||||
|
'@returns {variant} Element removed
|
||||||
|
Public Function Shift() As Variant
|
||||||
|
'Would be good to use CopyMemory here
|
||||||
|
|
||||||
|
CopyVariant Shift, pArr(1)
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To pLength - 1
|
||||||
|
pArr(i) = pArr(i + 1)
|
||||||
|
Next
|
||||||
|
pLength = pLength - 1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Insert an element onto the start of the array
|
||||||
|
'@param {Variant} Value to append to the start of the array
|
||||||
|
'@returns {stdArray} me
|
||||||
|
Public Function Unshift(val As Variant) As stdArray
|
||||||
|
'Would be good to use CopyMemory here
|
||||||
|
|
||||||
|
'Before Add event
|
||||||
|
Dim bCancel As Boolean
|
||||||
|
RaiseEvent BeforeAdd(Me, 1, val, bCancel)
|
||||||
|
If bCancel Then Exit Function
|
||||||
|
|
||||||
|
'Ensure array is big enough and increase pLength
|
||||||
|
If pLength = pProxyLength Then
|
||||||
|
pProxyLength = pProxyLength + pChunking
|
||||||
|
ReDim Preserve pArr(1 To pProxyLength) As Variant
|
||||||
|
End If
|
||||||
|
pLength = pLength + 1
|
||||||
|
|
||||||
|
'Unshift
|
||||||
|
For i = pLength - 1 To 1 Step -1
|
||||||
|
pArr(i + 1) = pArr(i)
|
||||||
|
Next
|
||||||
|
pArr(1) = val
|
||||||
|
|
||||||
|
'After Add event
|
||||||
|
RaiseEvent AfterAdd(Me, 1, val)
|
||||||
|
|
||||||
|
Set Unshift = Me
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'TODO:
|
||||||
|
'Public Function Slice() As stdArray
|
||||||
|
'
|
||||||
|
'End Function
|
||||||
|
|
||||||
|
'TODO:
|
||||||
|
'Public Function Splice() As stdArray
|
||||||
|
'
|
||||||
|
'End Function
|
||||||
|
|
||||||
|
'Creates a new instance of the same array
|
||||||
|
'@returns {stdArray}
|
||||||
|
Public Function Clone() As stdArray
|
||||||
|
If pInitialised Then
|
||||||
|
If pInitialised Then
|
||||||
|
'Similar to CreateFromArray() but passing length through also:
|
||||||
|
Set Clone = New stdArray
|
||||||
|
|
||||||
|
Call Clone.Init(pLength, 10)
|
||||||
|
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To pLength
|
||||||
|
Call Clone.Push(pArr(i))
|
||||||
|
Next
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
|
||||||
|
RaiseEvent AfterClone(Clone)
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Returns a new array with all elements in reverse order
|
||||||
|
'@returns {stdArray}
|
||||||
|
Public Function Reverse() As stdArray
|
||||||
|
'TODO: Need to find a better more low level approach to creating arrays from existing arrays/preventing redim for methods like this
|
||||||
|
Dim ret As stdArray
|
||||||
|
Set ret = stdArray.Create()
|
||||||
|
For i = pLength To 1 Step -1
|
||||||
|
Call ret.Push(pArr(i))
|
||||||
|
Next
|
||||||
|
Set Reverse = ret
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Concatenate an existing array of elements onto the end of this array
|
||||||
|
'@param {stdArray} Array whose elements we wish to append to the end of this array
|
||||||
|
'@returns {stdArray} New composite array.
|
||||||
|
Public Function Concat(ByVal arr As stdArray) As stdArray
|
||||||
|
Dim x As stdArray
|
||||||
|
Set x = Clone()
|
||||||
|
|
||||||
|
If Not arr Is Nothing Then
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To arr.Length
|
||||||
|
Call x.Push(arr.item(i))
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
|
||||||
|
Set Concat = x
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Join each of the elements of this array together as a string
|
||||||
|
'@param {string} Delimiter to insert between strings
|
||||||
|
Public Function Join(Optional ByVal delimeter As String = ",") As String
|
||||||
|
If pInitialised Then
|
||||||
|
If pLength > 0 Then
|
||||||
|
Dim sOutput As String
|
||||||
|
sOutput = pArr(1)
|
||||||
|
|
||||||
|
Dim i As Long
|
||||||
|
For i = 2 To pLength
|
||||||
|
sOutput = sOutput & delimeter & pArr(i)
|
||||||
|
Next
|
||||||
|
Join = sOutput
|
||||||
|
Else
|
||||||
|
Join = ""
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Get/Let/Set item
|
||||||
|
'@param {long} The location to get/set the item
|
||||||
|
Public Property Get item(ByVal i As Long) As Variant
|
||||||
|
'item(1) = 1st element
|
||||||
|
'item(2) = 2nd element
|
||||||
|
'etc.
|
||||||
|
CopyVariant item, pArr(i)
|
||||||
|
|
||||||
|
End Property
|
||||||
|
Public Property Set item(ByVal i As Long, ByVal item As Object)
|
||||||
|
Set pArr(i) = item
|
||||||
|
End Property
|
||||||
|
Public Property Let item(ByVal i As Long, ByVal item As Variant)
|
||||||
|
pArr(i) = item
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'Copy a variant into the array's ith element. This saves from having to test the item and call the correct `set` keyword
|
||||||
|
'@param {Long} The index at which the item's data should be set
|
||||||
|
'@param {ByRef Variant} Item to set at the index
|
||||||
|
Public Sub PutItem(ByVal i As Long, ByRef item As Variant)
|
||||||
|
CopyVariant pArr(i), item
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Obtain the index of an element
|
||||||
|
'@param {Variant} Element to find
|
||||||
|
'@param {Ingeger?=1} Location to start search for element.
|
||||||
|
'@returns {long} Index of element
|
||||||
|
Public Function indexOf(ByVal el As Variant, Optional ByVal start As Long = 1) As Long
|
||||||
|
Dim elIsObj As Boolean, i As Long, item As Variant, itemIsObj As Boolean
|
||||||
|
|
||||||
|
'Is element an object?
|
||||||
|
elIsObj = isObject(el)
|
||||||
|
|
||||||
|
'Loop over contents starting from start
|
||||||
|
For i = start To pLength
|
||||||
|
'Get item data
|
||||||
|
CopyVariant item, pArr(i)
|
||||||
|
|
||||||
|
'Is item an object?
|
||||||
|
itemIsObj = isObject(item)
|
||||||
|
|
||||||
|
'If both item and el are objects (must be the same type in order to be the same data)
|
||||||
|
If itemIsObj And elIsObj Then
|
||||||
|
If item Is el Then 'check items equal
|
||||||
|
indexOf = i 'return item index
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
'If both item and el are not objects (must be the same type in order to be the same data)
|
||||||
|
ElseIf Not itemIsObj And Not elIsObj Then
|
||||||
|
If item = el Then 'check items equal
|
||||||
|
indexOf = i 'return item index
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Return -1 i.e. no match found
|
||||||
|
indexOf = -1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Obtain the last index of an element
|
||||||
|
'@param {Variant} Element to find
|
||||||
|
'@returns {long} Last index of element
|
||||||
|
Public Function lastIndexOf(ByVal el As Variant)
|
||||||
|
Dim elIsObj As Boolean, i As Long, item As Variant, itemIsObj As Boolean
|
||||||
|
|
||||||
|
'Is element an object?
|
||||||
|
elIsObj = isObject(el)
|
||||||
|
|
||||||
|
'Loop over contents starting from start
|
||||||
|
For i = pLength To 1 Step -1
|
||||||
|
'Get item data
|
||||||
|
CopyVariant item, pArr(i)
|
||||||
|
|
||||||
|
'Is item an object?
|
||||||
|
itemIsObj = isObject(item)
|
||||||
|
|
||||||
|
'If both item and el are objects (must be the same type in order to be the same data)
|
||||||
|
If itemIsObj And elIsObj Then
|
||||||
|
If item Is el Then 'check items equal
|
||||||
|
lastIndexOf = i 'return item index
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
'If both item and el are not objects (must be the same type in order to be the same data)
|
||||||
|
ElseIf Not itemIsObj And Not elIsObj Then
|
||||||
|
If item = el Then 'check items equal
|
||||||
|
lastIndexOf = i 'return item index
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Return -1 i.e. no match found
|
||||||
|
lastIndexOf = -1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Returns true if the array contains an item
|
||||||
|
'@param {Variant} Item to find
|
||||||
|
'@param {long?=1} Index to start search for item at. (Internally uses indexOf())
|
||||||
|
Public Function includes(ByVal el As Variant, Optional ByVal startFrom As Long = 1) As Boolean
|
||||||
|
includes = indexOf(el, startFrom) >= startFrom
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'Iterative Functions (All require stdICallable):
|
||||||
|
|
||||||
|
'Example: if incidents.IsEvery(cbValid) then ...
|
||||||
|
Public Function IsEvery(ByVal cb As stdICallable) As Boolean
|
||||||
|
If pInitialised Then
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To pLength
|
||||||
|
Dim bFlag As Boolean
|
||||||
|
bFlag = cb.Run(pArr(i))
|
||||||
|
|
||||||
|
If Not bFlag Then
|
||||||
|
IsEvery = False
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
|
||||||
|
IsEvery = True
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function IsSome(ByVal cb As stdICallable) As Boolean
|
||||||
|
If pInitialised Then
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To pLength
|
||||||
|
Dim bFlag As Boolean
|
||||||
|
bFlag = cb.Run(pArr(i))
|
||||||
|
|
||||||
|
If bFlag Then
|
||||||
|
IsSome = True
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
IsSome = False
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Sub ForEach(ByVal cb As stdICallable)
|
||||||
|
If pInitialised Then
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To pLength
|
||||||
|
Call cb.Run(pArr(i))
|
||||||
|
Next
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Map(ByVal cb As stdICallable) As stdArray
|
||||||
|
If pInitialised Then
|
||||||
|
Dim pMap As stdArray
|
||||||
|
Set pMap = Clone()
|
||||||
|
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To pLength
|
||||||
|
'BUGFIX: Sometimes required, not sure when
|
||||||
|
Dim v As Variant
|
||||||
|
CopyVariant v, item(i)
|
||||||
|
|
||||||
|
'Call callback
|
||||||
|
Call pMap.PutItem(i, cb.Run(v))
|
||||||
|
Next
|
||||||
|
|
||||||
|
Set Map = pMap
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
'OPTIMISE: Needs optimisation. Currently very sub-optimal
|
||||||
|
Public Function Unique(Optional ByVal cb As stdICallable = Nothing) As stdArray
|
||||||
|
Dim ret As stdArray: Set ret = stdArray.CreateWithOptions(pLength, pChunking)
|
||||||
|
Dim retL As stdArray: Set retL = CreateWithOptions(pLength, pChunking)
|
||||||
|
|
||||||
|
'Collect keys
|
||||||
|
Dim vKeys As stdArray
|
||||||
|
If cb Is Nothing Then
|
||||||
|
Set vKeys = Clone()
|
||||||
|
Else
|
||||||
|
Set vKeys = Map(cb)
|
||||||
|
End If
|
||||||
|
|
||||||
|
'Unique by key
|
||||||
|
For i = 1 To pLength
|
||||||
|
If Not retL.includes(vKeys.item(i)) Then
|
||||||
|
Call retL.Push(vKeys.item(i))
|
||||||
|
Call ret.Push(pArr(i))
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Return data
|
||||||
|
Set Unique = ret
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function reduce(ByVal cb As stdICallable, Optional ByVal initialValue As Variant) As Variant
|
||||||
|
Dim iStart As Long
|
||||||
|
If pInitialised Then
|
||||||
|
If pLength > 0 Then
|
||||||
|
If IsMissing(initialValue) Then
|
||||||
|
Call CopyVariant(reduce, pArr(1))
|
||||||
|
iStart = 2
|
||||||
|
Else
|
||||||
|
Call CopyVariant(reduce, initialValue)
|
||||||
|
iStart = 1
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
If IsMissing(initialValue) Then
|
||||||
|
reduce = Empty
|
||||||
|
Else
|
||||||
|
Call CopyVariant(reduce, initialValue)
|
||||||
|
End If
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim i As Long
|
||||||
|
For i = iStart To pLength
|
||||||
|
'BUGFIX: Sometimes required, not sure when
|
||||||
|
Dim el As Variant
|
||||||
|
CopyVariant el, pArr(i)
|
||||||
|
|
||||||
|
'Reduce
|
||||||
|
CopyVariant reduce, cb.Run(reduce, el)
|
||||||
|
Next
|
||||||
|
Else
|
||||||
|
'Error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
Public Function Filter(ByVal cb As stdICallable) As stdArray
|
||||||
|
Dim ret As stdArray
|
||||||
|
Set ret = stdArray.CreateWithOptions(pLength, pChunking)
|
||||||
|
Set Filter = ret
|
||||||
|
|
||||||
|
'If initialised...
|
||||||
|
If pInitialised Then
|
||||||
|
Dim i As Long, v As Variant
|
||||||
|
'Loop over array
|
||||||
|
For i = 1 To pLength
|
||||||
|
'If callback succeeds, push retvar
|
||||||
|
If cb.Run(pArr(i)) Then
|
||||||
|
Call ret.Push(pArr(i))
|
||||||
|
End If
|
||||||
|
Next i
|
||||||
|
Else
|
||||||
|
'error
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function count(Optional ByVal cb As stdICallable = Nothing) As Long
|
||||||
|
If cb Is Nothing Then
|
||||||
|
count = Length
|
||||||
|
Else
|
||||||
|
Dim i As Long, lCount As Long
|
||||||
|
lCount = 0
|
||||||
|
For i = 1 To pLength
|
||||||
|
If cb.Run(pArr(i)) Then
|
||||||
|
lCount = lCount + 1
|
||||||
|
End If
|
||||||
|
Next i
|
||||||
|
count = lCount
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function groupBy(ByVal cb As stdICallable) As Object
|
||||||
|
'Array to store result in
|
||||||
|
Dim result As Object
|
||||||
|
Set result = CreateObject("Scripting.Dictionary")
|
||||||
|
|
||||||
|
'Loop over items
|
||||||
|
Dim i As Long
|
||||||
|
For i = 1 To pLength
|
||||||
|
'Get grouping key
|
||||||
|
Dim key As Variant
|
||||||
|
key = cb.Run(pArr(i))
|
||||||
|
|
||||||
|
'If key is not set then set it
|
||||||
|
If Not result.Exists(key) Then Set result(key) = stdArray.Create()
|
||||||
|
|
||||||
|
'Push item to key
|
||||||
|
result(key).Push pArr(i)
|
||||||
|
Next
|
||||||
|
|
||||||
|
'Return result
|
||||||
|
Set groupBy = result
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Max(Optional ByVal cb As stdICallable = Nothing, Optional ByVal startingValue As Variant = Empty) As Variant
|
||||||
|
Dim vRet, vMaxValue, v
|
||||||
|
vMaxValue = startingValue: vRet = startingValue
|
||||||
|
For i = 1 To pLength
|
||||||
|
Call CopyVariant(v, pArr(i))
|
||||||
|
|
||||||
|
'Get value to test
|
||||||
|
Dim vtValue As Variant
|
||||||
|
If cb Is Nothing Then
|
||||||
|
Call CopyVariant(vtValue, v)
|
||||||
|
Else
|
||||||
|
Call CopyVariant(vtValue, cb.Run(v))
|
||||||
|
End If
|
||||||
|
|
||||||
|
'Compare values and return
|
||||||
|
If IsEmpty(vRet) Then
|
||||||
|
Call CopyVariant(vRet, v)
|
||||||
|
Call CopyVariant(vMaxValue, vtValue)
|
||||||
|
ElseIf vMaxValue < vtValue Then
|
||||||
|
Call CopyVariant(vRet, v)
|
||||||
|
Call CopyVariant(vMaxValue, vtValue)
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
|
||||||
|
Call CopyVariant(Max, vRet)
|
||||||
|
End Function
|
||||||
|
Public Function Min(Optional ByVal cb As stdICallable = Nothing, Optional ByVal startingValue As Variant = Empty) As Variant
|
||||||
|
Dim vRet, vMinValue, v
|
||||||
|
vMinValue = startingValue: vRet = startingValue
|
||||||
|
For i = 1 To pLength
|
||||||
|
Call CopyVariant(v, pArr(i))
|
||||||
|
|
||||||
|
'Get value to test
|
||||||
|
Dim vtValue As Variant
|
||||||
|
If cb Is Nothing Then
|
||||||
|
Call CopyVariant(vtValue, v)
|
||||||
|
Else
|
||||||
|
Call CopyVariant(vtValue, cb.Run(v))
|
||||||
|
End If
|
||||||
|
|
||||||
|
'Compare values and return
|
||||||
|
If IsEmpty(vRet) Then
|
||||||
|
Call CopyVariant(vRet, v)
|
||||||
|
Call CopyVariant(vMinValue, vtValue)
|
||||||
|
ElseIf vMinValue > vtValue Then
|
||||||
|
Call CopyVariant(vRet, v)
|
||||||
|
Call CopyVariant(vMinValue, vtValue)
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
|
||||||
|
Call CopyVariant(Min, vRet)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'Copies one variant to a destination
|
||||||
|
'@param {ByRef Variant} dest Destination to copy variant to
|
||||||
|
'@param {Variant} value Source to copy variant from.
|
||||||
|
'@perf This appears to be a faster variant of "oleaut32.dll\VariantCopy" + it's multi-platform
|
||||||
|
Private Sub CopyVariant(ByRef dest As Variant, ByVal value As Variant)
|
||||||
|
If isObject(value) Then
|
||||||
|
Set dest = value
|
||||||
|
Else
|
||||||
|
dest = value
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
Attribute VB_Name = "stdICallable"
|
||||||
|
|
||||||
|
'Call will call the passed function with param array
|
||||||
|
Public Function Run(ParamArray params() As Variant) As Variant: End Function
|
||||||
|
|
||||||
|
'Call function with supplied array of params
|
||||||
|
Public Function RunEx(ByVal params As Variant) As Variant: End Function
|
||||||
|
|
||||||
|
'Bind a parameter to the function
|
||||||
|
Public Function Bind(ParamArray params() As Variant) As stdICallable: End Function
|
||||||
|
|
||||||
|
'Making late-bound calls to stdICallable members
|
||||||
|
'@protected
|
||||||
|
'@param {ByVal String} - Message to send
|
||||||
|
'@param {ByRef Boolean} - Whether the call was successful
|
||||||
|
'@param {ByVal Variant} - Any variant, typically parameters as an array. Passed along with the message.
|
||||||
|
'@returns {Variant} - Any return value.
|
||||||
|
Public Function SendMessage(ByVal sMessage As String, ByRef success As Boolean, ByVal params As Variant) As Variant: End Function
|
||||||
|
|
||||||
|
'Ideally we would want to get a pointer to the function... However, getting a pointer to an object method is
|
||||||
|
'going to be defficult, partly due to the first parameter sent to the function is `Me`! We'll likely have to
|
||||||
|
'use machine code to wrap a call with a `Me` pointer just so we can access the full pointer and use this in
|
||||||
|
'real life applications.
|
||||||
|
'Finally it might be better to do something more like: `stdPointer.fromICallable()` anyway
|
||||||
|
''Returns a callback function
|
||||||
|
''Typically this will be achieved with `stdPointer.GetLastPrivateMethod(me)`
|
||||||
|
''If this cannot be implemented return 0
|
||||||
|
'Public Function ToPointer() as long
|
||||||
|
|
||||||
|
''Bind arguments to functions to appear as first arguments in call.
|
||||||
|
''e.g. stdLambda.Create("$1.EnableEvents = false: $1.ScreenUpdating = false").bind(Application).Run()
|
||||||
|
'Public Function Bind(ByVal v as variant) as stdICallable: End Function
|
||||||
|
|
||||||
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,56 @@
|
|||||||
|
Attribute VB_Name = "工作表1"
|
||||||
|
Private Sub CommandButton21_Click()
|
||||||
|
' Application.Caption = PYEAR & Worksheets("售價").Cells(3, 1)
|
||||||
|
Dim oSB As Object
|
||||||
|
Set oSB = CreateObject("System.Text.StringBuilder")
|
||||||
|
|
||||||
|
oSB.AppendFormat_5 Nothing, "hello {0}", Array("simon")
|
||||||
|
Debug.Assert oSB.ToString = "hello simon"
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Private Sub sample()
|
||||||
|
' Dim exl As Excel.Application
|
||||||
|
' Dim wb As Excel.Workbook
|
||||||
|
' Dim i As Integer
|
||||||
|
' Dim j As Integer
|
||||||
|
' Dim cnt As Integer
|
||||||
|
' Dim cn As New ADODB.Connection
|
||||||
|
' Dim t1 As Date
|
||||||
|
' Dim t2 As Date
|
||||||
|
' Dim t As Date
|
||||||
|
'
|
||||||
|
' t1 = Now
|
||||||
|
'
|
||||||
|
' Set exl = CreateObject("Excel.Application")
|
||||||
|
' Set wb = exl.Workbooks.Add
|
||||||
|
'
|
||||||
|
' exl.ActiveWorkbook.SaveAs (Excel.ThisWorkbook.Path & "\sample3.xls")
|
||||||
|
' exl.ActiveWorkbook.Close
|
||||||
|
' exl.Quit
|
||||||
|
'
|
||||||
|
' cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
|
||||||
|
' "Data Source=" & Excel.ThisWorkbook.Path & "\sample3.xls;" & _
|
||||||
|
' "Extended Properties=""Excel 8.0;HDR=YES;IMEX=0"""
|
||||||
|
'
|
||||||
|
' cn.Execute "CREATE TABLE [工作表1$] (R INT, G INT, B INT)"
|
||||||
|
' cn.Execute "UPDATE [工作表1$] SET R = 0"
|
||||||
|
' cn.Execute "UPDATE [工作表1$] SET G = 1"
|
||||||
|
' cn.Execute "UPDATE [工作表1$] SET B = 2"
|
||||||
|
'
|
||||||
|
' cnt = 3
|
||||||
|
' For i = 1 To 10000
|
||||||
|
' cn.Execute "INSERT INTO [工作表1$] (R,G,B) VALUES (" & cnt & "," & cnt + 1 & "," & cnt + 2 & ")"
|
||||||
|
' cnt = cnt + 3
|
||||||
|
' Next i
|
||||||
|
'
|
||||||
|
' cn.Close
|
||||||
|
' Set cn = Nothing
|
||||||
|
' t2 = Now
|
||||||
|
'
|
||||||
|
' t = t2 - t1
|
||||||
|
'
|
||||||
|
' MsgBox Second(t)
|
||||||
|
'End Sub
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
Attribute VB_Name = "工作表10"
|
||||||
|
'**************** 程式變數 ******************
|
||||||
|
Const PYEAR = 2023
|
||||||
|
Const PCOMPANY = 27
|
||||||
|
Const strXLSfile = "\index.xlsb" '資料來源
|
||||||
|
Const intFieldAmount = 20 '表中欄位數目
|
||||||
|
Const ITEMcount = 9 '處理品項數目
|
||||||
|
Const MSC = 5 '男生班級行數
|
||||||
|
Const MSD = 7 '男生小計行數
|
||||||
|
Const WSC = 13 '女生班級行數
|
||||||
|
Const WSD = 15 '女生小計行數
|
||||||
|
'********************************************
|
||||||
|
|
||||||
|
Private Sub Worksheet_Activate()
|
||||||
|
Dim p As Long
|
||||||
|
Dim diag As New ProgressDialogue
|
||||||
|
Application.Calculation = xlManual '設置手動重算
|
||||||
|
p = 0
|
||||||
|
diag.Configure "更新資料時間", "Now wasting your time...", 0, ITEMcount
|
||||||
|
diag.Show
|
||||||
|
For i = 1 To ITEMcount
|
||||||
|
Call subtotal(i, 1, 3)
|
||||||
|
Call subtotal(i, 2, 3)
|
||||||
|
diag.SetValue p
|
||||||
|
diag.SetStatus "Now wasting your time... " & p
|
||||||
|
p = p + 1
|
||||||
|
Next i
|
||||||
|
diag.Hide
|
||||||
|
Application.Calculation = xlAutomatic '設置自動重算
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub subtotal(strField As Variant, intRange As Variant, intRow As Variant)
|
||||||
|
' strField 小計使用欄位
|
||||||
|
' intRange 男、女 1,2
|
||||||
|
' intRow 填入列數
|
||||||
|
Dim rstSubtotal As ADODB.Recordset
|
||||||
|
Dim strSQL As String
|
||||||
|
Dim intRowLoc As Integer
|
||||||
|
Dim fldClass, fldAmount As Field
|
||||||
|
Dim cellClass, cellData As Range
|
||||||
|
|
||||||
|
Dim Cnmy As ADODB.Connection
|
||||||
|
Set Cnmy = Cnmyopen()
|
||||||
|
Set rstSubtotal = New ADODB.Recordset
|
||||||
|
strSQL = "SELECT departclass, sum(item" & strField & ") " & _
|
||||||
|
"FROM clothing.BUYANDSELL " & _
|
||||||
|
"where departclass<>'0' and " & Choose(intRange, "cus_sex=true and ", "cus_sex=false and ", "") & _
|
||||||
|
"sid between " & PYEAR & PCOMPANY & "0000 AND " & PYEAR & PCOMPANY & "9999" & _
|
||||||
|
" group by department;"
|
||||||
|
' Debug.Print strSQL
|
||||||
|
' rstSubtotal.CursorLocation = 3 'mysql RecordCount連線BUG by 藍色小舖 老頑童
|
||||||
|
rstSubtotal.Open strSQL, Cnmy
|
||||||
|
|
||||||
|
Set fldClass = rstSubtotal.Fields(0) '項目尺寸
|
||||||
|
Set fldAmount = rstSubtotal.Fields(1) '項目尺寸數量
|
||||||
|
For intI = 1 To intFieldAmount '行填入位置
|
||||||
|
Set cellClass = Range(Cells(intRow + (strField - 1) * 21 + intI - 1, IIf(intRange = 1, MSC, WSC)), _
|
||||||
|
Cells(intRow + (strField - 1) * 21 + intI - 1, IIf(intRange = 1, MSC, WSC))) '欄位名稱
|
||||||
|
Set cellData = Range(Cells(intRow + (strField - 1) * 21 + intI - 1, IIf(intRange = 1, MSD, WSD)), _
|
||||||
|
Cells(intRow + (strField - 1) * 21 + intI - 1, IIf(intRange = 1, MSD, WSD))) '欄位資料
|
||||||
|
If Not rstSubtotal.EOF Then
|
||||||
|
cellClass.value = fldClass
|
||||||
|
cellData.value = fldAmount
|
||||||
|
rstSubtotal.MoveNext
|
||||||
|
Else
|
||||||
|
'清除項目內容
|
||||||
|
cellClass.ClearContents
|
||||||
|
cellData.ClearContents
|
||||||
|
End If
|
||||||
|
Next intI
|
||||||
|
|
||||||
|
rstSubtotal.Close
|
||||||
|
Set rstSubtotal = Nothing
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
Attribute VB_Name = "工作表11"
|
||||||
|
Private Const ROW_RESIZE_COUNT As Long = 1000
|
||||||
|
Private Const OUTPUT_COL_START As Long = 3
|
||||||
|
Private Const OUTPUT_COL_END As Long = 25
|
||||||
|
|
||||||
|
Private Enum OutputMode
|
||||||
|
OutputModePerson = 1
|
||||||
|
OutputModeBS = 2
|
||||||
|
OutputModeWS = 3
|
||||||
|
OutputModeLS = 4
|
||||||
|
OutputModeWP = 5
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
' 目前模組內未使用,保留供未來擴充
|
||||||
|
Private Enum ValueMode
|
||||||
|
ValueModeCount = 1
|
||||||
|
ValueModeSum = 2
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
' 輸出設定結構:集中管理各模式對應的欄位與索引
|
||||||
|
Private Type OutputConfig
|
||||||
|
ClearRows As Variant ' 要清除內容的列號陣列
|
||||||
|
ClassRowMale As Long ' 男生標籤列
|
||||||
|
DataRowMale As Long ' 男生資料列
|
||||||
|
ClassRowFemale As Long ' 女生標籤列
|
||||||
|
DataRowFemale As Long ' 女生資料列
|
||||||
|
GenderIndex As Long ' 分解 key 後,性別所在索引 (0-based)
|
||||||
|
LabelIndex As Long ' 分解 key 後,標籤所在索引 (0-based)
|
||||||
|
SourceLabelCol As Long ' 來源工作表中的標籤欄位
|
||||||
|
SourceGenderCol As Long ' 來源工作表中的性別欄位
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Private Sub Worksheet_Activate()
|
||||||
|
Me.Cells(1, 1).value = "Update " & Date
|
||||||
|
|
||||||
|
Dim startTime As Double
|
||||||
|
startTime = Timer
|
||||||
|
|
||||||
|
Application.Calculation = xlManual
|
||||||
|
Call XLS_init
|
||||||
|
|
||||||
|
Call CalcClassCounts(OutputModePerson)
|
||||||
|
' Call CalcClassCounts(OutputModeBS, 1)
|
||||||
|
' Call CalcClassCounts(OutputModeWS, 2)
|
||||||
|
' Call CalcClassCounts(OutputModeLS, 3)
|
||||||
|
' Call CalcClassCounts(OutputModeWP, 4)
|
||||||
|
|
||||||
|
Application.Calculation = xlAutomatic
|
||||||
|
|
||||||
|
Me.Cells(6, 1).value = "執行時間:" & Format(Timer - startTime, "0.00") & " 秒"
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CalcClassCounts(mode As Variant, Optional strField As Variant)
|
||||||
|
' strField 保留給未來小計欄位擴充使用
|
||||||
|
Dim dictSubtotal As Object
|
||||||
|
Set dictSubtotal = ProcessCustomerData(mode, strField)
|
||||||
|
WriteOutputCommon dictSubtotal, mode
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub WriteOutputCommon(ByVal dataDict As Object, ByVal mode As OutputMode)
|
||||||
|
Dim dataKey As Variant
|
||||||
|
Dim dataValue As Variant
|
||||||
|
Dim arrWords As Variant
|
||||||
|
Dim cfg As OutputConfig
|
||||||
|
Dim cellClass As Range
|
||||||
|
Dim cellData As Range
|
||||||
|
Dim classRow As Long
|
||||||
|
Dim dataRow As Long
|
||||||
|
Dim currentLabel As String
|
||||||
|
Dim colIndex As Long
|
||||||
|
Dim sortedKeys As Variant
|
||||||
|
Dim i As Long
|
||||||
|
|
||||||
|
On Error GoTo WriteOutputError
|
||||||
|
|
||||||
|
If dataDict Is Nothing Or dataDict.count = 0 Then Exit Sub
|
||||||
|
|
||||||
|
cfg = GetOutputConfig(mode)
|
||||||
|
ClearOutputRows cfg.ClearRows
|
||||||
|
|
||||||
|
colIndex = 2 ' 第一個標籤會變成 3,對應欄位 C
|
||||||
|
currentLabel = vbNullString
|
||||||
|
sortedKeys = SortKeys(dataDict.keys)
|
||||||
|
|
||||||
|
For i = LBound(sortedKeys) To UBound(sortedKeys)
|
||||||
|
dataKey = sortedKeys(i)
|
||||||
|
arrWords = Split(dataKey, "_")
|
||||||
|
dataValue = dataDict(dataKey)
|
||||||
|
|
||||||
|
If UBound(arrWords) < 1 Then
|
||||||
|
Debug.Print "警告: 數據格式錯誤: " & dataKey
|
||||||
|
GoTo NextItem
|
||||||
|
End If
|
||||||
|
|
||||||
|
' 標籤改變時換下一欄
|
||||||
|
If CStr(arrWords(cfg.LabelIndex)) <> currentLabel Then
|
||||||
|
colIndex = colIndex + 1
|
||||||
|
currentLabel = CStr(arrWords(cfg.LabelIndex))
|
||||||
|
End If
|
||||||
|
|
||||||
|
If arrWords(cfg.GenderIndex) = "女" Then
|
||||||
|
classRow = cfg.ClassRowFemale
|
||||||
|
dataRow = cfg.DataRowFemale
|
||||||
|
Else
|
||||||
|
classRow = cfg.ClassRowMale
|
||||||
|
dataRow = cfg.DataRowMale
|
||||||
|
End If
|
||||||
|
|
||||||
|
Set cellClass = Me.Cells(classRow, colIndex)
|
||||||
|
Set cellData = Me.Cells(dataRow, colIndex)
|
||||||
|
cellClass.value = arrWords(cfg.LabelIndex)
|
||||||
|
cellData.value = dataValue
|
||||||
|
|
||||||
|
NextItem:
|
||||||
|
Next i
|
||||||
|
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
WriteOutputError:
|
||||||
|
MsgBox "寫入輸出時發生錯誤: " & Err.Description, vbExclamation, "寫入錯誤"
|
||||||
|
Exit Sub
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub ClearOutputRows(ByVal rows As Variant)
|
||||||
|
Dim idx As Long
|
||||||
|
|
||||||
|
For idx = LBound(rows) To UBound(rows)
|
||||||
|
Me.Range(Me.Cells(rows(idx), OUTPUT_COL_START), Me.Cells(rows(idx), OUTPUT_COL_END)).ClearContents
|
||||||
|
Next idx
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Function GetOutputConfig(ByVal mode As OutputMode) As OutputConfig
|
||||||
|
Dim cfg As OutputConfig
|
||||||
|
|
||||||
|
Select Case mode
|
||||||
|
Case OutputModePerson
|
||||||
|
cfg.ClearRows = Array(2, 3, 4)
|
||||||
|
cfg.ClassRowMale = 2
|
||||||
|
cfg.DataRowMale = 3
|
||||||
|
cfg.ClassRowFemale = 2
|
||||||
|
cfg.DataRowFemale = 4
|
||||||
|
cfg.GenderIndex = 1
|
||||||
|
cfg.LabelIndex = 0
|
||||||
|
cfg.SourceLabelCol = 4
|
||||||
|
cfg.SourceGenderCol = 7
|
||||||
|
Case OutputModeBS
|
||||||
|
cfg.ClearRows = Array(9, 10, 11)
|
||||||
|
cfg.ClassRowMale = 9
|
||||||
|
cfg.DataRowMale = 10
|
||||||
|
cfg.ClassRowFemale = 9
|
||||||
|
cfg.DataRowFemale = 11
|
||||||
|
cfg.GenderIndex = 1
|
||||||
|
cfg.LabelIndex = 0
|
||||||
|
cfg.SourceLabelCol = 12
|
||||||
|
cfg.SourceGenderCol = 5
|
||||||
|
Case OutputModeWS
|
||||||
|
cfg.ClearRows = Array(16, 17, 18)
|
||||||
|
cfg.ClassRowMale = 16
|
||||||
|
cfg.DataRowMale = 17
|
||||||
|
cfg.ClassRowFemale = 16
|
||||||
|
cfg.DataRowFemale = 18
|
||||||
|
cfg.GenderIndex = 1
|
||||||
|
cfg.LabelIndex = 0
|
||||||
|
cfg.SourceLabelCol = 13
|
||||||
|
cfg.SourceGenderCol = 5
|
||||||
|
Case OutputModeLS
|
||||||
|
cfg.ClearRows = Array(33, 36, 38, 41)
|
||||||
|
cfg.ClassRowMale = 33
|
||||||
|
cfg.DataRowMale = 36
|
||||||
|
cfg.ClassRowFemale = 38
|
||||||
|
cfg.DataRowFemale = 41
|
||||||
|
cfg.GenderIndex = 0
|
||||||
|
cfg.LabelIndex = 1
|
||||||
|
cfg.SourceLabelCol = 0 ' TODO: 請填入實際欄位
|
||||||
|
cfg.SourceGenderCol = 0 ' TODO: 請填入實際欄位
|
||||||
|
Case OutputModeWP
|
||||||
|
cfg.ClearRows = Array(45, 48, 50, 53)
|
||||||
|
cfg.ClassRowMale = 45
|
||||||
|
cfg.DataRowMale = 48
|
||||||
|
cfg.ClassRowFemale = 50
|
||||||
|
cfg.DataRowFemale = 53
|
||||||
|
cfg.GenderIndex = 0
|
||||||
|
cfg.LabelIndex = 1
|
||||||
|
cfg.SourceLabelCol = 0 ' TODO: 請填入實際欄位
|
||||||
|
cfg.SourceGenderCol = 0 ' TODO: 請填入實際欄位
|
||||||
|
Case Else
|
||||||
|
Err.Raise vbObjectError + 1, "GetOutputConfig", "未知的輸出模式"
|
||||||
|
End Select
|
||||||
|
|
||||||
|
GetOutputConfig = cfg
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ProcessCustomerData(mode As Variant, Optional strField As Variant) As Object
|
||||||
|
Dim dict As Object
|
||||||
|
Dim sourceRange As Variant
|
||||||
|
Dim wsSource As Worksheet
|
||||||
|
Dim i As Long
|
||||||
|
Dim cfg As OutputConfig
|
||||||
|
Dim labelValue As Variant
|
||||||
|
Dim genderValue As Variant
|
||||||
|
Dim dictKey As String
|
||||||
|
|
||||||
|
Set dict = CreateObject("Scripting.Dictionary")
|
||||||
|
Set wsSource = ActiveWorkbook.Worksheets("帳務")
|
||||||
|
sourceRange = wsSource.Range("Cu帳務").Resize(ROW_RESIZE_COUNT)
|
||||||
|
cfg = GetOutputConfig(mode)
|
||||||
|
|
||||||
|
For i = 1 To UBound(sourceRange, 1)
|
||||||
|
If Not IsEmpty(sourceRange(i, 1)) Then
|
||||||
|
labelValue = sourceRange(i, cfg.SourceLabelCol)
|
||||||
|
genderValue = sourceRange(i, cfg.SourceGenderCol)
|
||||||
|
|
||||||
|
If cfg.LabelIndex = 0 Then
|
||||||
|
dictKey = labelValue & "_" & genderValue
|
||||||
|
Else
|
||||||
|
dictKey = genderValue & "_" & labelValue
|
||||||
|
End If
|
||||||
|
|
||||||
|
SetDictionaryValue dict, dictKey, 1
|
||||||
|
End If
|
||||||
|
Next i
|
||||||
|
|
||||||
|
Set ProcessCustomerData = dict
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Sub SetDictionaryValue(ByRef dict As Object, ByVal key As Variant, ByVal value As Variant)
|
||||||
|
If dict.Exists(key) Then
|
||||||
|
dict(key) = dict(key) + value
|
||||||
|
Else
|
||||||
|
dict.Add key, value
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' 以氣泡排序法回傳排序後的 key 陣列,避免依賴外部 stdArray 函式庫
|
||||||
|
Private Function SortKeys(keys As Variant) As Variant
|
||||||
|
Dim arr() As Variant
|
||||||
|
Dim i As Long
|
||||||
|
Dim j As Long
|
||||||
|
Dim temp As Variant
|
||||||
|
Dim n As Long
|
||||||
|
|
||||||
|
n = UBound(keys) - LBound(keys) + 1
|
||||||
|
If n <= 1 Then
|
||||||
|
SortKeys = keys
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
ReDim arr(LBound(keys) To UBound(keys))
|
||||||
|
For i = LBound(keys) To UBound(keys)
|
||||||
|
arr(i) = keys(i)
|
||||||
|
Next i
|
||||||
|
|
||||||
|
For i = LBound(arr) To UBound(arr) - 1
|
||||||
|
For j = i + 1 To UBound(arr)
|
||||||
|
If arr(i) > arr(j) Then
|
||||||
|
temp = arr(i)
|
||||||
|
arr(i) = arr(j)
|
||||||
|
arr(j) = temp
|
||||||
|
End If
|
||||||
|
Next j
|
||||||
|
Next i
|
||||||
|
|
||||||
|
SortKeys = arr
|
||||||
|
End Function
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
Attribute VB_Name = "工作表14"
|
||||||
|
Private Sub Worksheet_Activate()
|
||||||
|
Dim startTime As Double, totalRows As Long
|
||||||
|
Dim dict As Object
|
||||||
|
Dim itemAddup As New stdArray
|
||||||
|
Dim changeRange As Variant
|
||||||
|
Dim tmp As stdArray
|
||||||
|
Dim oArr As Variant, pArr As Variant, qArr As Variant
|
||||||
|
Dim i As Long, arrWords As Variant
|
||||||
|
Dim key As Variant, vIter As Variant
|
||||||
|
|
||||||
|
' 創建字典
|
||||||
|
Set dict = CreateObject("Scripting.Dictionary")
|
||||||
|
|
||||||
|
' 設置範圍
|
||||||
|
changeRange = Me.Range("ChangeTBL").Resize(100)
|
||||||
|
|
||||||
|
' 開始計時
|
||||||
|
startTime = Timer
|
||||||
|
|
||||||
|
' 填充字典
|
||||||
|
For i = 1 To UBound(changeRange)
|
||||||
|
If Not IsEmpty(changeRange(i, 1)) Then
|
||||||
|
Call FillDictionary(dict, changeRange(i, 1) & "_" & changeRange(i, 2), changeRange(i, 3))
|
||||||
|
End If
|
||||||
|
Next i
|
||||||
|
|
||||||
|
' 將字典中的項目添加到 itemAddup
|
||||||
|
Set itemAddup = stdArray.Create
|
||||||
|
For Each key In dict.keys
|
||||||
|
itemAddup.Push key & "_" & dict(key)
|
||||||
|
Next key
|
||||||
|
totalRows = dict.count
|
||||||
|
|
||||||
|
' 初始化輸出範圍的數組
|
||||||
|
Me.Range("O3").CurrentRegion.ClearContents
|
||||||
|
oArr = Me.Range("O3").Resize(totalRows)
|
||||||
|
pArr = Me.Range("P3").Resize(totalRows)
|
||||||
|
qArr = Me.Range("Q3").Resize(totalRows)
|
||||||
|
|
||||||
|
' 排序並將值填充到臨時數組
|
||||||
|
Set tmp = stdArray.Create
|
||||||
|
itemAddup.Sort().ForEach stdLambda.Create("$1.push($2)").Bind(tmp)
|
||||||
|
|
||||||
|
' 迭代臨時數組並將值填充到相應的欄位
|
||||||
|
i = 1
|
||||||
|
For Each vIter In tmp
|
||||||
|
arrWords = Split(vIter, "_")
|
||||||
|
oArr(i, 1) = arrWords(0)
|
||||||
|
pArr(i, 1) = arrWords(1)
|
||||||
|
qArr(i, 1) = arrWords(2)
|
||||||
|
i = i + 1
|
||||||
|
Next vIter
|
||||||
|
|
||||||
|
' 顯示執行時間
|
||||||
|
Me.Range("O1").value = Timer - startTime
|
||||||
|
|
||||||
|
' 將結果寫回工作表
|
||||||
|
Me.Range("O3").Resize(totalRows).value = oArr
|
||||||
|
Me.Range("P3").Resize(totalRows).value = pArr
|
||||||
|
Me.Range("Q3").Resize(totalRows).value = qArr
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub FillDictionary(ByRef dict As Object, item As Variant, itemValue As Variant)
|
||||||
|
If dict.Exists(item) Then
|
||||||
|
dict(item) = dict(item) + itemValue
|
||||||
|
Else
|
||||||
|
dict.Add item, itemValue
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Attribute VB_Name = "工作表15"
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Attribute VB_Name = "工作表2"
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Attribute VB_Name = "工作表3"
|
||||||
|
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
Attribute VB_Name = "工作表4"
|
||||||
|
'**************** 程式變數 **********************
|
||||||
|
Const PYEAR = 2024 '年度
|
||||||
|
Const PCOMPANY = 27
|
||||||
|
Const strXLSfile = "\index.xlsb" '資料來源
|
||||||
|
'************************************************
|
||||||
|
|
||||||
|
Private Sub CommandButton21_Click()
|
||||||
|
Dim strSQL As String
|
||||||
|
On Error GoTo Err_cmdYes_Click
|
||||||
|
|
||||||
|
' 匯入資料
|
||||||
|
Call updSELLUP
|
||||||
|
|
||||||
|
Exit_cmdYes_Click:
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
Err_cmdYes_Click:
|
||||||
|
MsgBox Err.Description
|
||||||
|
Resume Exit_cmdYes_Click
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' 匯入量身資料
|
||||||
|
Public Sub updSELLUP()
|
||||||
|
'====================================+
|
||||||
|
Dim p As Long '|
|
||||||
|
Dim diag As New ProgressDialogue '|
|
||||||
|
'====================================+
|
||||||
|
Dim sb As StringBuilder
|
||||||
|
Set sb = New StringBuilder
|
||||||
|
Dim rstItem As New ADODB.Recordset
|
||||||
|
Dim rstTBL As New ADODB.Recordset 'excel資料表
|
||||||
|
Dim strSQL, strSQLitem As String
|
||||||
|
Dim fld As ADODB.Field
|
||||||
|
Dim Cnxn, Cnmy As ADODB.Connection
|
||||||
|
Set Cnmy = Cnmyopen()
|
||||||
|
Set Cnxn = Cnxnopen(XLSfile, "1")
|
||||||
|
|
||||||
|
sb.Append "Select " & PYEAR & PCOMPANY & " & Format([編號],'0000') AS ID,[班ID] as depart,[性別] as sex" & _
|
||||||
|
", [校短尺碼] as itsz1, [校短訂購] as item1, [校長尺碼] as itsz2, [校長訂購] as item2, [校夏尺碼] as itsz3, [校夏訂購] as item3" & _
|
||||||
|
", [校冬尺碼] as itsz4, [校冬訂購] as item4, [校裙尺碼] as itsz5, [校裙訂購] as item5" & _
|
||||||
|
", [背心尺碼] as itsz6, [背心訂購] as item6, 0 as itsz7, [書包訂購] as item7, 0 as itsz8, [領帶訂購] as item8, 0 as itsz9, [腰帶訂購] as item9" & _
|
||||||
|
" FROM [帳務$];"
|
||||||
|
' Debug.Print sb.toString
|
||||||
|
With rstTBL
|
||||||
|
.Open sb.ToString, Cnxn, adOpenStatic, adLockOptimistic, adCmdText
|
||||||
|
lastdepart = !depart
|
||||||
|
'====================================================================================+
|
||||||
|
p = 0 '|
|
||||||
|
diag.Configure "更新資料時間", "Now wasting your time...", 0, .RecordCount '|
|
||||||
|
diag.Show '|
|
||||||
|
'====================================================================================+
|
||||||
|
strSQLitem = "SELECT pno,mtmodel,mtname,mtcolor,wtmodel,wtname,wtcolor,tomeasure FROM GoodsUse" & _
|
||||||
|
" WHERE corp_use=" & PCOMPANY & " and Depart_Use=" & !depart & _
|
||||||
|
" order by pno;"
|
||||||
|
rstItem.Open strSQLitem, Cnmy
|
||||||
|
While Not (.EOF Or diag.cancelIsPressed)
|
||||||
|
If Not lastdepart = !depart Then
|
||||||
|
rstItem.Close
|
||||||
|
strSQLitem = "SELECT pno,mtmodel,mtname,mtcolor,wtmodel,wtname,wtcolor,tomeasure FROM GoodsUse" & _
|
||||||
|
" WHERE corp_use=" & PCOMPANY & " and Depart_Use=" & !depart & _
|
||||||
|
" order by pno;"
|
||||||
|
rstItem.Open strSQLitem, Cnmy
|
||||||
|
' Range("AF3").CopyFromRecordset rstItem
|
||||||
|
End If
|
||||||
|
i = 1
|
||||||
|
rstItem.MoveFirst
|
||||||
|
sb.Clear
|
||||||
|
sb.Append "UPDATE SELLUP SET "
|
||||||
|
'資料來源動態設定
|
||||||
|
While Not rstItem.EOF
|
||||||
|
sb.Append "itsz" & i & "=goodsid('" & .Fields.item("itsz" & i) & "'," & IIf(!sex = "男", rstItem!mtmodel, rstItem!wtmodel) & ")," & _
|
||||||
|
"item" & i & "=" & .Fields.item("item" & i)
|
||||||
|
i = i + 1
|
||||||
|
rstItem.MoveNext
|
||||||
|
If Not rstItem.EOF Then sb.Append ","
|
||||||
|
Wend
|
||||||
|
sb.Append " WHERE id=" & !ID & ";"
|
||||||
|
Debug.Print sb.ToString
|
||||||
|
' Cnmy.Execute sb.toString
|
||||||
|
lastdepart = !depart
|
||||||
|
.MoveNext
|
||||||
|
'================================================+
|
||||||
|
diag.SetValue p '|
|
||||||
|
diag.SetStatus "Now wasting your time... " & p '|
|
||||||
|
p = p + 1 '|
|
||||||
|
'================================================+
|
||||||
|
Wend
|
||||||
|
'============+
|
||||||
|
diag.Hide '|
|
||||||
|
'============+
|
||||||
|
.Close
|
||||||
|
End With
|
||||||
|
Cnxn.Close
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Attribute VB_Name = "工作表5"
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Attribute VB_Name = "工作表6"
|
||||||
|
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
Attribute VB_Name = "工作表7"
|
||||||
|
'**************** 程式變數 ******************
|
||||||
|
Const strXLSfile = "\index.xlsm" '資料來源
|
||||||
|
Const idxStart = 1810
|
||||||
|
Const idxEnd = 1849
|
||||||
|
Const idxRange = "argRange"
|
||||||
|
'********************************************
|
||||||
|
|
||||||
|
Private Sub CommandButton1_Click()
|
||||||
|
Dim sb As StringBuilder
|
||||||
|
Set sb = New StringBuilder
|
||||||
|
|
||||||
|
Dim strSQL As String
|
||||||
|
On Error GoTo Err_cmdYes_Click
|
||||||
|
Dim Cnmy As ADODB.Connection
|
||||||
|
Set Cnmy = Cnmyopen()
|
||||||
|
|
||||||
|
'清除舊資料
|
||||||
|
sb.Append "delete FROM DEPARTMENT where id BETWEEN " & idxStart & " AND " & idxEnd & ";"
|
||||||
|
Cnmy.Execute sb.ToString '刪除站點
|
||||||
|
' 更新資料
|
||||||
|
Call renewDepart(Cnmy)
|
||||||
|
|
||||||
|
Exit_cmdYes_Click:
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
Err_cmdYes_Click:
|
||||||
|
MsgBox Err.Description
|
||||||
|
Resume Exit_cmdYes_Click
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub renewDepart(Cnmy As ADODB.Connection)
|
||||||
|
'*************************************
|
||||||
|
Dim p As Long '*
|
||||||
|
Dim diag As New ProgressDialogue '*
|
||||||
|
'*************************************
|
||||||
|
' 更新站點資料
|
||||||
|
Dim sb As StringBuilder
|
||||||
|
Set sb = New StringBuilder
|
||||||
|
|
||||||
|
Dim Cnxn As ADODB.Connection
|
||||||
|
Dim rstTBL As New ADODB.Recordset
|
||||||
|
Dim strSQL As String
|
||||||
|
|
||||||
|
Set Cnxn = Cnxnopen(strXLSfile, "1")
|
||||||
|
|
||||||
|
sb.Append "Select 代碼 as id, 科別 as title, 代碼 as depart FROM [引數$" & _
|
||||||
|
Replace(Sheets("引數").Range(idxRange).Address, "$", "") & "];"
|
||||||
|
' Debug.Print sb.toString
|
||||||
|
rstTBL.Open sb.ToString, Cnxn, adOpenStatic, adLockOptimistic, adCmdText
|
||||||
|
'*************************************************************************************
|
||||||
|
p = 0 '*
|
||||||
|
diag.Configure "Wasting Time", "Now wasting your time...", 0, rstTBL.RecordCount '*
|
||||||
|
diag.Show '*
|
||||||
|
'*************************************************************************************
|
||||||
|
sb.Clear
|
||||||
|
sb.Append "INSERT INTO DEPARTMENT(id,title,depart) VALUES "
|
||||||
|
|
||||||
|
While Not (rstTBL.EOF Or diag.cancelIsPressed)
|
||||||
|
If Not IsNull(rstTBL.Fields.item("title")) Then
|
||||||
|
sb.Append "(" & rstTBL.Fields.item("id") & ",'"
|
||||||
|
sb.Append rstTBL.Fields.item("title") & "','"
|
||||||
|
sb.Append rstTBL.Fields.item("depart") & "')"
|
||||||
|
End If
|
||||||
|
rstTBL.MoveNext
|
||||||
|
If Not rstTBL.EOF Then
|
||||||
|
sb.Append ","
|
||||||
|
End If
|
||||||
|
'*************************************************
|
||||||
|
diag.SetValue p '*
|
||||||
|
diag.SetStatus "Now wasting your time... " & p '*
|
||||||
|
p = p + 1 '*
|
||||||
|
'*************************************************
|
||||||
|
Wend
|
||||||
|
' Debug.Print sb.toString
|
||||||
|
Cnmy.Execute sb.ToString
|
||||||
|
rstTBL.Close
|
||||||
|
'*************
|
||||||
|
diag.Hide '*
|
||||||
|
'*************
|
||||||
|
End Sub
|
||||||
|
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
Attribute VB_Name = "工作表8"
|
||||||
|
'**************** 程式變數 ******************
|
||||||
|
Const PYEAR = 2024
|
||||||
|
Const PCOMPANY = 27
|
||||||
|
Const strXLSfile = "\index.xlsb" '資料來源
|
||||||
|
Const FieldAmount = 23 '表中欄位數目
|
||||||
|
Const srtRange = "wordSort" '排序名稱
|
||||||
|
Const ITEMcount = 9 '處理品項數目
|
||||||
|
Dim SELLITEM()
|
||||||
|
|
||||||
|
Private Sub Worksheet_Activate()
|
||||||
|
'********************************************
|
||||||
|
' === 使用品項,欄位,行數 ===
|
||||||
|
SELLITEM() = Array(Array("", 0, True) _
|
||||||
|
, Array("校短", 9, True) _
|
||||||
|
, Array("校長", 21, True) _
|
||||||
|
, Array("校夏", 33, True) _
|
||||||
|
, Array("校冬", 45, True) _
|
||||||
|
, Array("校裙", 57, True) _
|
||||||
|
, Array("背心", 69, True) _
|
||||||
|
, Array("領帶", 81, False) _
|
||||||
|
, Array("腰帶", 89, False) _
|
||||||
|
, Array("書包", 97, False))
|
||||||
|
'********************************************
|
||||||
|
'====================================+
|
||||||
|
Dim p As Long '|
|
||||||
|
Dim diag As New ProgressDialogue '|
|
||||||
|
'====================================+
|
||||||
|
Call XLS_init
|
||||||
|
Application.Calculation = xlManual '設置手動重算
|
||||||
|
|
||||||
|
Cells(1, 1).value = "統計" & Date
|
||||||
|
Call statClass(2, 3) '人數統計
|
||||||
|
'====================================================================================+
|
||||||
|
p = 0 '|
|
||||||
|
diag.Configure "更新資料時間", "Now wasting your time...", 0, ITEMcount '|
|
||||||
|
diag.Show '|
|
||||||
|
'====================================================================================+
|
||||||
|
For i = 1 To ITEMcount
|
||||||
|
Call QueryItem(SELLITEM(i)(0), SELLITEM(i)(1), SELLITEM(i)(2))
|
||||||
|
'================================================+
|
||||||
|
diag.SetValue p '|
|
||||||
|
diag.SetStatus "Now wasting your time... " & p '|
|
||||||
|
p = p + 1 '|
|
||||||
|
'================================================+
|
||||||
|
Next i
|
||||||
|
'============+
|
||||||
|
diag.Hide '|
|
||||||
|
'============+
|
||||||
|
Application.Calculation = xlAutomatic '設置自動重算
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub QueryItem(strItem As Variant, intRow As Variant, blnNosize As Variant)
|
||||||
|
' strItem 項目
|
||||||
|
' intRow 填表行位置
|
||||||
|
Dim rstEmployees As New ADODB.Recordset
|
||||||
|
Dim strSQLEmployees As String
|
||||||
|
Dim Cnxn As ADODB.Connection
|
||||||
|
Set Cnxn = Cnxnopen(XLSfile, "1")
|
||||||
|
If blnNosize = True Then
|
||||||
|
strSQLEmployees = "SELECT [" & strItem & "尺碼],sum([" & strItem & "訂購]) as people" & _
|
||||||
|
" FROM [帳務$]A LEFT JOIN [引數$" & Replace(Sheets("引數").Range(srtRange).Address, "$", "") & _
|
||||||
|
"]B ON A.[" & strItem & "尺碼]=B.[排序]" & _
|
||||||
|
" where [性別]='男'" & _
|
||||||
|
" group by [" & strItem & "尺碼],B.[序號]" & _
|
||||||
|
" ORDER BY B.[序號];"
|
||||||
|
' Debug.Print strSQLEmployees
|
||||||
|
rstEmployees.Open strSQLEmployees, Cnxn, adOpenStatic, adLockOptimistic, adCmdText
|
||||||
|
Call QueryItemEx1(rstEmployees, "男" & strItem, intRow)
|
||||||
|
rstEmployees.Close
|
||||||
|
strSQLEmployees = "SELECT [" & strItem & "尺碼],sum([" & strItem & "訂購]) as people" & _
|
||||||
|
" FROM [帳務$]A LEFT JOIN [引數$" & Replace(Sheets("引數").Range(srtRange).Address, "$", "") & _
|
||||||
|
"]B ON A.[" & strItem & "尺碼]=B.[排序]" & _
|
||||||
|
" where [性別]='女'" & _
|
||||||
|
" group by [" & strItem & "尺碼],B.[序號]" & _
|
||||||
|
" ORDER BY B.[序號];"
|
||||||
|
' Debug.Print strSQLEmployees
|
||||||
|
rstEmployees.Open strSQLEmployees, Cnxn, adOpenStatic, adLockOptimistic, adCmdText
|
||||||
|
Call QueryItemEx1(rstEmployees, "女" & strItem, intRow + 5)
|
||||||
|
Else
|
||||||
|
strSQLEmployees = "SELECT [班級],sum(iif([性別]='男',[" & strItem & "訂購],0)) as boys,sum(iif([性別]='女',[" & strItem & "訂購],0)) as girls" & _
|
||||||
|
" FROM [帳務$]" & _
|
||||||
|
" group by [班級]" & _
|
||||||
|
" ORDER BY [班級];"
|
||||||
|
' Debug.Print strSQLEmployees
|
||||||
|
rstEmployees.Open strSQLEmployees, Cnxn, adOpenStatic, adLockOptimistic, adCmdText
|
||||||
|
Call QueryItemEx2(rstEmployees, strItem, intRow)
|
||||||
|
End If
|
||||||
|
rstEmployees.Close
|
||||||
|
Set rstEmployees = Nothing
|
||||||
|
Cnxn.Close
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub QueryItemEx1(rstEmployees As ADODB.Recordset, strItem As Variant, intRow As Variant)
|
||||||
|
' rstEmployees 統計資料
|
||||||
|
' strItem 項目
|
||||||
|
' intRow 填表行位置
|
||||||
|
Dim intRowLoc As Integer
|
||||||
|
Dim fldSize, fldPeoples As Field
|
||||||
|
Dim cellsName, cellsData1 As Range
|
||||||
|
'填入項目的列數
|
||||||
|
For intJ = 1 To WorksheetFunction.RoundUp(rstEmployees.RecordCount / FieldAmount, 0) '行數目限制
|
||||||
|
If Not intJ = 1 Then 'intJ表一範圍
|
||||||
|
intRowLoc = intRow + (12 * (intJ - 1))
|
||||||
|
Else
|
||||||
|
intRowLoc = intRow
|
||||||
|
End If
|
||||||
|
Cells(intRowLoc, 1).value = strItem '項目名稱
|
||||||
|
Set fldSize = rstEmployees.Fields(0)
|
||||||
|
Set fldPeoples = rstEmployees.Fields(1)
|
||||||
|
For intI = 1 To FieldAmount '行填入位置
|
||||||
|
Set cellsName = Range(Cells(intRowLoc, 2 + intI), Cells(intRowLoc, 2 + intI)) '欄位名稱
|
||||||
|
Set cellsData1 = Range(Cells(intRowLoc + 3, 2 + intI), Cells(intRowLoc + 3, 2 + intI)) '欄位資料
|
||||||
|
If Not rstEmployees.EOF Then
|
||||||
|
cellsName.value = IIf(fldSize = "0", "未定", fldSize)
|
||||||
|
cellsData1.value = fldPeoples
|
||||||
|
rstEmployees.MoveNext
|
||||||
|
Else
|
||||||
|
'清除項目內容
|
||||||
|
cellsName.ClearContents
|
||||||
|
cellsData1.ClearContents
|
||||||
|
End If
|
||||||
|
Next intI
|
||||||
|
Next intJ
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub QueryItemEx2(rstEmployees As ADODB.Recordset, strItem As Variant, intRow As Variant)
|
||||||
|
' rstEmployees 統計資料
|
||||||
|
' strItem 項目
|
||||||
|
' intRow 填表行位置
|
||||||
|
Dim intRowLoc As Integer
|
||||||
|
Dim fldClass, fldBoys, fldGirls As Field
|
||||||
|
Dim cellsName, cellsData1 As Range
|
||||||
|
'填入項目的列數
|
||||||
|
For intJ = 1 To WorksheetFunction.RoundUp(rstEmployees.RecordCount / FieldAmount, 0) '行數目限制
|
||||||
|
If Not intJ = 1 Then 'intJ表一範圍
|
||||||
|
intRowLoc = intRow + (12 * (intJ - 1))
|
||||||
|
Else
|
||||||
|
intRowLoc = intRow
|
||||||
|
End If
|
||||||
|
Cells(intRowLoc, 1).value = strItem '項目名稱
|
||||||
|
Set fldClass = rstEmployees.Fields(0)
|
||||||
|
Set fldBoys = rstEmployees.Fields(1)
|
||||||
|
Set fldGirls = rstEmployees.Fields(2)
|
||||||
|
For intI = 1 To FieldAmount '行填入位置
|
||||||
|
Set cellsName = Range(Cells(intRowLoc, 2 + intI), Cells(intRowLoc, 2 + intI)) '欄位名稱
|
||||||
|
Set cellsData1 = Range(Cells(intRowLoc + 3, 2 + intI), Cells(intRowLoc + 3, 2 + intI)) '欄位資料
|
||||||
|
Set cellsData2 = Range(Cells(intRowLoc + 4, 2 + intI), Cells(intRowLoc + 4, 2 + intI)) '欄位資料
|
||||||
|
If Not rstEmployees.EOF Then
|
||||||
|
cellsName.value = IIf(fldClass = "0", "未定", fldClass)
|
||||||
|
cellsData1.value = fldBoys
|
||||||
|
cellsData2.value = fldGirls
|
||||||
|
rstEmployees.MoveNext
|
||||||
|
Else
|
||||||
|
'清除項目內容
|
||||||
|
cellsName.ClearContents
|
||||||
|
cellsData1.ClearContents
|
||||||
|
cellsData2.ClearContents
|
||||||
|
End If
|
||||||
|
Next intI
|
||||||
|
Next intJ
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub statClass(intRow As Variant, intDataRow As Variant)
|
||||||
|
' intRow 填表行位置
|
||||||
|
' intDataRow 填資料行列數
|
||||||
|
Dim rstClass As New ADODB.Recordset
|
||||||
|
Dim strSQLclass As String
|
||||||
|
Dim intRowLoc As Integer
|
||||||
|
Dim fldClass, fldBoys, fldGirls As Field
|
||||||
|
Dim cellsName, cellsData1, cellsData2 As Range
|
||||||
|
Dim Cnxn As ADODB.Connection
|
||||||
|
Set Cnxn = Cnxnopen(XLSfile, "1")
|
||||||
|
|
||||||
|
strSQLclass = "SELECT [班級],sum(iif([性別]='男',1,0)) as boys,sum(iif([性別]='女',1,0)) as girls" & _
|
||||||
|
" FROM [帳務$]" & _
|
||||||
|
" group by [班級];"
|
||||||
|
' Debug.Print strSQLclass
|
||||||
|
rstClass.Open strSQLclass, Cnxn, adOpenStatic, adLockOptimistic, adCmdText
|
||||||
|
Set fldClass = rstClass.Fields(0)
|
||||||
|
Set fldBoys = rstClass.Fields(1)
|
||||||
|
Set fldGirls = rstClass.Fields(2)
|
||||||
|
For intI = 1 To FieldAmount '行填入位置
|
||||||
|
Set cellsName = Range(Cells(intRow, 2 + intI), Cells(intRow, 2 + intI)) '欄位名稱
|
||||||
|
Set cellsData1 = Range(Cells(intDataRow, 2 + intI), Cells(intDataRow, 2 + intI)) '欄位資料
|
||||||
|
Set cellsData2 = Range(Cells(intDataRow + 1, 2 + intI), Cells(intDataRow + 1, 2 + intI)) '欄位資料
|
||||||
|
If Not rstClass.EOF Then
|
||||||
|
cellsName.value = fldClass
|
||||||
|
cellsData1.value = fldBoys
|
||||||
|
cellsData2.value = fldGirls
|
||||||
|
rstClass.MoveNext
|
||||||
|
Else
|
||||||
|
'清除項目內容
|
||||||
|
cellsName.ClearContents
|
||||||
|
cellsData1.ClearContents
|
||||||
|
cellsData2.ClearContents
|
||||||
|
End If
|
||||||
|
Next intI
|
||||||
|
rstClass.Close
|
||||||
|
Cnxn.Close
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
Attribute VB_Name = "工作表9"
|
||||||
|
'**************** 程式變數 ******************
|
||||||
|
'Const PYEAR = 2020 '年度
|
||||||
|
'Const PCOMPANY = 11 '公司代碼
|
||||||
|
'Const strXLSfile = "\制服.xlsm" '資料來源
|
||||||
|
'Const strLookup = "中油嘉義" '使用程式的學校
|
||||||
|
Const ITEMcount = 5 '匯入處理品項數目
|
||||||
|
Const intFieldAmount = 22 '表中欄位數目
|
||||||
|
Const xlsRANGE = "[引數$J1:K23]" '排序引數表中位置
|
||||||
|
Const intFLeft = 23 '填入表格左邊界
|
||||||
|
|
||||||
|
Private Sub CommandButton1_Click()
|
||||||
|
Dim strSQL As String
|
||||||
|
On Error GoTo Err_cmdYes_Click
|
||||||
|
|
||||||
|
Application.Calculation = xlManual '設置手動重算
|
||||||
|
'查詢欄位名稱table
|
||||||
|
Dim Cnxn As New ADODB.Connection
|
||||||
|
Set Cnxn = Cnxnopen(XLSfile, "1")
|
||||||
|
|
||||||
|
Range("W2:AF28").Clear '清除使用範圍儲存格
|
||||||
|
Call FfName(Cnxn, "校裙", 2)
|
||||||
|
' Call FfName(Cnxn, "短袖", 9)
|
||||||
|
' Call FfName(Cnxn, "長褲", 16)
|
||||||
|
' Call FfName(Cnxn, "夾克", 23)
|
||||||
|
'
|
||||||
|
Application.Calculation = xlAutomatic ' 設置自動重算
|
||||||
|
Cnxn.Close
|
||||||
|
Set Cnxn = Nothing
|
||||||
|
|
||||||
|
Exit_cmdYes_Click:
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
Err_cmdYes_Click:
|
||||||
|
MsgBox Err.Description
|
||||||
|
Resume Exit_cmdYes_Click
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub FfName(Cnxn As ADODB.Connection, strItem As String, intRow As Integer)
|
||||||
|
' intRange 男、女、全 1,2,3
|
||||||
|
' intRow 填表行位置
|
||||||
|
Dim rstFfname As New ADODB.Recordset
|
||||||
|
Dim strSQL As String
|
||||||
|
Dim intRowLoc As Integer
|
||||||
|
'select 尺碼,男人數,男數量,女人數,女數量,合計
|
||||||
|
strSQL = "Select " & strItem & "尺碼,sum(IIf(性別='男',1,0))," & "sum(IIf(性別='男'," & strItem & "訂購,0))," & _
|
||||||
|
"sum(IIf(性別='男',0,1)),sum(IIf(性別='男',0," & strItem & "訂購))," & "sum(" & strItem & "訂購)," & xlsRANGE & ".id " & _
|
||||||
|
"FROM [異動$] INNER JOIN " & xlsRANGE & " ON " & xlsRANGE & ".strorder = [異動$]." & strItem & "尺碼" & _
|
||||||
|
" WHERE isnull([異動$].員編)" & _
|
||||||
|
" GROUP BY " & xlsRANGE & ".id,[異動$]." & strItem & "尺碼" & _
|
||||||
|
" ORDER BY " & xlsRANGE & ".id;"
|
||||||
|
' Debug.Print strSQL
|
||||||
|
rstFfname.Open strSQL, Cnxn, adOpenStatic, adLockOptimistic, adCmdText
|
||||||
|
'填入項目
|
||||||
|
For intJ = 1 To WorksheetFunction.RoundUp(rstFfname.RecordCount / intFieldAmount, 0) '行數目限制
|
||||||
|
intRowLoc = IIf(intJ <> 1, intRow + (2 * (intJ - 1)), intRow) 'intJ表一範圍
|
||||||
|
For intI = 1 To intFieldAmount
|
||||||
|
If Not rstFfname.EOF Then
|
||||||
|
For intK = 0 To 5 '行填入位置
|
||||||
|
Cells(intRowLoc + intK, intFLeft + intI).Borders.LineStyle = xlContinuous
|
||||||
|
Cells(intRowLoc + intK, intFLeft + intI).value = rstFfname.Fields(intK)
|
||||||
|
Next intK
|
||||||
|
rstFfname.MoveNext
|
||||||
|
End If
|
||||||
|
Next intI
|
||||||
|
Next intJ
|
||||||
|
rstFfname.Close
|
||||||
|
Set rstFfname = Nothing
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in new issue
Block a user