ch3chi-commons-vue 1.2.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/api/ApiService.ts +869 -0
- package/src/auth/AuthorizationService.ts +138 -0
- package/src/auth/PermissionDescriptor.ts +99 -0
- package/src/auth/keys.ts +5 -0
- package/src/components/CAlert.vue +188 -0
- package/src/components/CAlertDefine.ts +20 -0
- package/src/components/CBSToast.vue +119 -0
- package/src/components/CGlobalSpinner.vue +84 -0
- package/src/components/CImage.vue +67 -0
- package/src/components/CRowCheckBox.vue +75 -0
- package/src/components/CRowTextInput.vue +27 -0
- package/src/components/CTable.vue +524 -0
- package/src/components/CTableDefine.ts +566 -0
- package/src/components/CTableTD.vue +28 -0
- package/src/components/HasPermission.vue +28 -0
- package/src/components/form/CChangePasswordFormField.vue +146 -0
- package/src/components/form/CCheckBoxFormField.vue +91 -0
- package/src/components/form/CCheckBoxPlatFormField.vue +94 -0
- package/src/components/form/CDateFormField.vue +149 -0
- package/src/components/form/CDateQueryField.vue +111 -0
- package/src/components/form/CDateRangeFormField.vue +138 -0
- package/src/components/form/CFilePickerFormField.vue +471 -0
- package/src/components/form/CRadioFormField.vue +62 -0
- package/src/components/form/CRadioPlatFormField.vue +67 -0
- package/src/components/form/CSelectFormField.vue +175 -0
- package/src/components/form/CTextAreaFormField.vue +84 -0
- package/src/components/form/CTextInputFormField.vue +99 -0
- package/src/components/form/CTinyMCEEditorFormField.vue +99 -0
- package/src/components/form/SCTextInputFormField.vue +129 -0
- package/src/composables/useCheckBoxFormField.ts +126 -0
- package/src/composables/useRadioFormField.ts +106 -0
- package/src/directive/CBootstrapDirective.ts +83 -0
- package/src/directive/CDateFormatterDirective.ts +37 -0
- package/src/directive/CFTurnstileDirective.ts +46 -0
- package/src/directive/CFormDirective.ts +57 -0
- package/src/directive/PermissionDirective.ts +102 -0
- package/src/env.d.ts +19 -0
- package/src/index.ts +83 -0
- package/src/model/BSFieldStyleConfig.ts +349 -0
- package/src/model/BaseDictionary.ts +86 -0
- package/src/model/BaseFormDataModel.ts +623 -0
- package/src/model/BaseListViewModel.ts +392 -0
- package/src/model/CBSModalViewModel.ts +91 -0
- package/src/model/CFileDataModel.ts +181 -0
- package/src/model/CImageViewModel.ts +34 -0
- package/src/model/CMenuItem.ts +199 -0
- package/src/model/EmailReceiverDataModel.ts +149 -0
- package/src/model/EmptyDataModel.ts +25 -0
- package/src/model/FormOptions.ts +112 -0
- package/src/model/LoginDataModel.ts +51 -0
- package/src/model/PasswordDataModel.ts +70 -0
- package/src/model/QueryParameter.ts +310 -0
- package/src/model/SessionUser.ts +110 -0
- package/src/model/ShowMessageDataModel.ts +69 -0
- package/src/model/TokenUser.ts +157 -0
- package/src/stores/FormDataStore.ts +73 -0
- package/src/stores/ViewStore.ts +701 -0
- package/src/stores/VueSessionStoreInstaller.ts +22 -0
- package/src/types/turnstile.d.ts +8 -0
- package/src/utils/CToolUtils.ts +133 -0
- package/dist/api/ApiService.d.ts +0 -233
- package/dist/auth/AuthorizationService.d.ts +0 -56
- package/dist/auth/PermissionDescriptor.d.ts +0 -37
- package/dist/components/CAlert.vue.d.ts +0 -17
- package/dist/components/CAlertDefine.d.ts +0 -14
- package/dist/components/CBSToast.vue.d.ts +0 -6
- package/dist/components/CGlobalSpinner.vue.d.ts +0 -13
- package/dist/components/CRowCheckBox.vue.d.ts +0 -14
- package/dist/components/CRowTextInput.vue.d.ts +0 -10
- package/dist/components/CTable.vue.d.ts +0 -24
- package/dist/components/CTableDefine.d.ts +0 -201
- package/dist/components/CTableTD.vue.d.ts +0 -7
- package/dist/components/form/CChangePasswordFormField.vue.d.ts +0 -14
- package/dist/components/form/CCheckBoxFormField.vue.d.ts +0 -30
- package/dist/components/form/CDateFormField.vue.d.ts +0 -17
- package/dist/components/form/CDateQueryField.vue.d.ts +0 -16
- package/dist/components/form/CDateRangeFormField.vue.d.ts +0 -17
- package/dist/components/form/CFilePickerFormField.vue.d.ts +0 -28
- package/dist/components/form/CRadioFormField.vue.d.ts +0 -30
- package/dist/components/form/CSelectFormField.vue.d.ts +0 -18
- package/dist/components/form/CTextAreaFormField.vue.d.ts +0 -16
- package/dist/components/form/CTextInputFormField.vue.d.ts +0 -22
- package/dist/directive/CBootstrapDirective.d.ts +0 -17
- package/dist/directive/CDateFormatterDirective.d.ts +0 -10
- package/dist/directive/CFTurnstileDirective.d.ts +0 -15
- package/dist/directive/CFormDirective.d.ts +0 -9
- package/dist/directive/PermissionDirective.d.ts +0 -15
- package/dist/index.cjs.js +0 -19103
- package/dist/index.d.ts +0 -45
- package/dist/index.es.js +0 -19086
- package/dist/model/BSFieldStyleConfig.d.ts +0 -121
- package/dist/model/BaseDictionary.d.ts +0 -34
- package/dist/model/BaseFormDataModel.d.ts +0 -199
- package/dist/model/BaseListViewModel.d.ts +0 -165
- package/dist/model/CBSModalViewModel.d.ts +0 -44
- package/dist/model/CFileDataModel.d.ts +0 -74
- package/dist/model/CImageViewModel.d.ts +0 -8
- package/dist/model/CMenuItem.d.ts +0 -86
- package/dist/model/EmailReceiverDataModel.d.ts +0 -57
- package/dist/model/EmptyDataModel.d.ts +0 -7
- package/dist/model/FormOptions.d.ts +0 -60
- package/dist/model/LoginDataModel.d.ts +0 -12
- package/dist/model/PasswordDataModel.d.ts +0 -15
- package/dist/model/QueryParameter.d.ts +0 -92
- package/dist/model/SessionUser.d.ts +0 -45
- package/dist/model/ShowMessageDataModel.d.ts +0 -44
- package/dist/model/TokenUser.d.ts +0 -50
- package/dist/stores/FormDataStore.d.ts +0 -31
- package/dist/stores/ViewStore.d.ts +0 -349
- package/dist/style.css +0 -223
- package/dist/utils/CToolUtils.d.ts +0 -53
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import {h, VNode} from 'vue';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import {lodashExTools as eTool} from "../utils/CToolUtils";
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
|
+
import {v4 as uuidv4} from 'uuid';
|
|
6
|
+
import CRowCheckBox from "./CRowCheckBox.vue";
|
|
7
|
+
import {CheckableModel} from "../model/BaseFormDataModel";
|
|
8
|
+
import CRowTextInput from "./CRowTextInput.vue";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* CTableColumnType 定義了表格欄位的類型
|
|
13
|
+
* - RowNumber: 行號類型
|
|
14
|
+
* - Text: 文字類型
|
|
15
|
+
* - Date: 日期類型
|
|
16
|
+
* - DateTime: 日期時間類型
|
|
17
|
+
* - Action: 操作類型
|
|
18
|
+
*/
|
|
19
|
+
export enum CTableColumnType {
|
|
20
|
+
RowNumber = 'rowNumber', // 行號類型
|
|
21
|
+
Text = 'text', // 文字類型
|
|
22
|
+
TextInput = 'textInput', // 文字輸入類型(可手動切換?)
|
|
23
|
+
Date = 'date', // 日期類型
|
|
24
|
+
DateRange = 'dateRange', // 日期範圍類型
|
|
25
|
+
DateTime = 'datetime', // 日期時間類型
|
|
26
|
+
Action = "action", // 操作類型
|
|
27
|
+
Checkbox = 'checkbox', // 複選框類型
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* CTableColumnActionType 定義了表格欄位的操作類型
|
|
32
|
+
*/
|
|
33
|
+
export enum CTableColumnActionType {
|
|
34
|
+
Edit = 'edit', // 編輯操作
|
|
35
|
+
Delete = 'delete', // 刪除操作
|
|
36
|
+
Review = 'review', // 審核操作
|
|
37
|
+
Vote = 'vote', // 投票操作
|
|
38
|
+
Check = 'check', // 勾選操作
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* CTableColumnActionParam 定義了表格欄位操作的參數
|
|
43
|
+
*/
|
|
44
|
+
export interface CTableColumnActionParam {
|
|
45
|
+
actionType: CTableColumnActionType, // 操作類型
|
|
46
|
+
rowData: Record<string, any>; // 當前行的數據
|
|
47
|
+
event?: Event; // 事件對象
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface CTableColumnActionMeta {
|
|
51
|
+
actionType: CTableColumnActionType,
|
|
52
|
+
text?: string; // 按鈕顯示文字
|
|
53
|
+
cls?: string; // 按鈕樣式類名
|
|
54
|
+
icon?: string; // 按鈕圖示類名
|
|
55
|
+
onClick?: (p: CTableColumnActionParam) => void; // 點擊
|
|
56
|
+
emit?: (event: string, ...args: any[]) => void; // vue 事件發射器
|
|
57
|
+
permission?: string | string[]; // 權限字串
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* CTableColumnDefine 定義了表格欄位的屬性和行為
|
|
62
|
+
*/
|
|
63
|
+
export interface CTableColumnDefine {
|
|
64
|
+
id?: string; // 欄位唯一識別符 用在 vue key
|
|
65
|
+
type: CTableColumnType; // 欄位類型
|
|
66
|
+
idName?: string; // 欄位鍵名
|
|
67
|
+
dataName?: string; // 資料鍵名
|
|
68
|
+
dataNameList?: string[]; // 資料鍵名列表,僅在 type 為 DateRange 時有效
|
|
69
|
+
text: string; // 欄位顯示文字
|
|
70
|
+
defaultText?: string; // 默認欄位顯示文字
|
|
71
|
+
sortConfig?: {
|
|
72
|
+
sortable?: boolean; // 是否可排序
|
|
73
|
+
key?: string | string[]; // 排序鍵名
|
|
74
|
+
direction?: 'asc' | 'desc'; // 預設排序方向
|
|
75
|
+
} | undefined,
|
|
76
|
+
width?: string; // 欄位寬度, 例如 '150px' 或 '20%',可選
|
|
77
|
+
outerWrapperCls?: string; // 外層容器 class 名稱,可選
|
|
78
|
+
align?: 'left' | 'center' | 'right'; // th 欄位對齊方式,可選
|
|
79
|
+
dataAlign?: 'left' | 'center' | 'right'; // td 資料對齊方式,可選
|
|
80
|
+
formatPattern?: string; // 格式化模式,例如日期格式 yyyy-MM-dd,使用 dayjs,可選
|
|
81
|
+
actionList?: CTableColumnActionMeta[] // 操作類型,僅在 type 為 Action 時有效,可選
|
|
82
|
+
actionFilter?: (action: CTableColumnActionMeta, dataItem?: any) => boolean; // 權限過濾函式
|
|
83
|
+
customRender?: (rowData: Record<string, any>) => string | HTMLElement | VNode; // 自定義
|
|
84
|
+
useHtml?: boolean; // 是否使用 HTML 渲染,預設為 false
|
|
85
|
+
useVNode?: boolean; // 是否使用 VNode 渲染,預設為 false
|
|
86
|
+
vNodeAttrs?: Record<string, any>; // VNode 屬性
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* CTableColumn 類別定義了表格欄位的屬性和行為
|
|
92
|
+
*/
|
|
93
|
+
export class CTableColumn implements CTableColumnDefine {
|
|
94
|
+
id?: string;
|
|
95
|
+
type: CTableColumnType = CTableColumnType.Text; // 欄位類型,默認為文字類型
|
|
96
|
+
idName?: string;
|
|
97
|
+
dataName?: string;
|
|
98
|
+
dataNameList?: string[]; // 資料鍵名列表,僅在 type 為 DateRange 時有效
|
|
99
|
+
text: string = ''; // 欄位顯示文字
|
|
100
|
+
defaultText?: string = ''; // 默認欄位顯示文字
|
|
101
|
+
sortConfig?: {
|
|
102
|
+
sortable?: boolean;
|
|
103
|
+
key?: string | string[];
|
|
104
|
+
direction?: 'asc' | 'desc';
|
|
105
|
+
};
|
|
106
|
+
width?: string;
|
|
107
|
+
outerWrapperCls?: string;
|
|
108
|
+
align?: 'left' | 'center' | 'right' = 'left'; // th 欄位對齊方式
|
|
109
|
+
dataAlign?: 'left' | 'center' | 'right' = 'left'; // td 資料對齊方式
|
|
110
|
+
formatPattern?: string;
|
|
111
|
+
actionList?: CTableColumnActionMeta[];
|
|
112
|
+
actionFilter?: (action: CTableColumnActionMeta, dataItem?: any) => boolean;
|
|
113
|
+
defaultRender?: (rowData: Record<string, any>) => string | HTMLElement | VNode; // 默認渲染函數
|
|
114
|
+
customRender?: (rowData: Record<string, any>) => string | HTMLElement | VNode;
|
|
115
|
+
useHtml: boolean = false;
|
|
116
|
+
useVNode: boolean = false;
|
|
117
|
+
vNodeAttrs?: Record<string, any>; // VNode 屬性
|
|
118
|
+
|
|
119
|
+
constructor(define: CTableColumnDefine) {
|
|
120
|
+
Object.assign(this, define);
|
|
121
|
+
if (_.isNil(this.id) || _.isEmpty(this.id)) {
|
|
122
|
+
// 如果沒有提供 id,則生成一個唯一的 id
|
|
123
|
+
this.id = uuidv4();
|
|
124
|
+
}
|
|
125
|
+
// Ensure sortConfig exists and provide backward compatibility for legacy top-level props
|
|
126
|
+
if (!this.sortConfig) {
|
|
127
|
+
this.sortConfig = { sortable: false };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.defaultRender = this.makeDefaultRender();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ~ ----------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
static defaultIconMap: Record<CTableColumnActionType, string> = {
|
|
136
|
+
[CTableColumnActionType.Edit]: 'edit',
|
|
137
|
+
[CTableColumnActionType.Delete]: 'trash-alt',
|
|
138
|
+
[CTableColumnActionType.Review]: 'magnifying-glass',
|
|
139
|
+
[CTableColumnActionType.Vote]: 'magnifying-glass',
|
|
140
|
+
[CTableColumnActionType.Check]: '',
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
static defaultBtnClassMap: Record<CTableColumnActionType, string> = {
|
|
144
|
+
[CTableColumnActionType.Edit]: 'btn btn-link',
|
|
145
|
+
[CTableColumnActionType.Delete]: 'btn btn-default text-danger',
|
|
146
|
+
[CTableColumnActionType.Review]: 'btn btn-link',
|
|
147
|
+
[CTableColumnActionType.Vote]: 'btn btn-outline-primary',
|
|
148
|
+
[CTableColumnActionType.Check]: '',
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// 允許使用者覆寫預設值
|
|
152
|
+
static configure(config: {
|
|
153
|
+
iconMap?: Partial<Record<CTableColumnActionType, string>>;
|
|
154
|
+
btnClassMap?: Partial<Record<CTableColumnActionType, string>>;
|
|
155
|
+
}) {
|
|
156
|
+
if (config.iconMap) {
|
|
157
|
+
this.defaultIconMap = { ...this.defaultIconMap, ...config.iconMap };
|
|
158
|
+
}
|
|
159
|
+
if (config.btnClassMap) {
|
|
160
|
+
this.defaultBtnClassMap = { ...this.defaultBtnClassMap, ...config.btnClassMap };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ~ ----------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 渲染欄位內容
|
|
168
|
+
* @param rowData 當前行的數據
|
|
169
|
+
*/
|
|
170
|
+
render(rowData: Record<string, any>): string | HTMLElement | VNode {
|
|
171
|
+
if (this.customRender) {
|
|
172
|
+
return this.customRender(rowData);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (this.defaultRender) {
|
|
176
|
+
return this.defaultRender(rowData);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 如果沒有自定義渲染,則返回該欄位的值
|
|
180
|
+
const value = eTool.getVal(rowData, this.dataName, '');
|
|
181
|
+
return String(value); // 返回字符串形式的值
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 生成 th 的 class 名稱
|
|
186
|
+
*/
|
|
187
|
+
thClass(): string {
|
|
188
|
+
// 根據欄位對齊方式生成 th 的 class 名稱
|
|
189
|
+
let cls = 'c-table-column';
|
|
190
|
+
if (this.align) {
|
|
191
|
+
cls += ` text-${this.align}`;
|
|
192
|
+
}
|
|
193
|
+
// 如果欄位可排序,添加 sortable 類名
|
|
194
|
+
if (this.sortConfig?.sortable) {
|
|
195
|
+
cls += ' c-sortable';
|
|
196
|
+
}
|
|
197
|
+
return cls;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 生成 td 的 class 名稱
|
|
202
|
+
*/
|
|
203
|
+
tdClass(): string {
|
|
204
|
+
// 根據欄位對齊方式生成 td 的 class 名稱
|
|
205
|
+
let cls = 'c-table-column';
|
|
206
|
+
if (this.dataAlign) {
|
|
207
|
+
cls += ` text-${this.dataAlign}`;
|
|
208
|
+
}
|
|
209
|
+
return cls;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 處理欄位操作
|
|
214
|
+
* @private
|
|
215
|
+
*/
|
|
216
|
+
private handleAction = new Proxy(
|
|
217
|
+
(actionMeta: CTableColumnActionMeta, rowData: Record<string, any>, event?: Event) => {
|
|
218
|
+
// 這裡可以根據 actionType 做不同處理
|
|
219
|
+
// 執行 callback 函數
|
|
220
|
+
actionMeta.onClick?.({
|
|
221
|
+
actionType: actionMeta.actionType,
|
|
222
|
+
rowData: rowData,
|
|
223
|
+
event: event
|
|
224
|
+
});
|
|
225
|
+
// fire action event 'table-action'
|
|
226
|
+
actionMeta.emit?.('table-action', actionMeta.actionType, rowData, event);
|
|
227
|
+
}, {});
|
|
228
|
+
|
|
229
|
+
// ~ ----------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* 生成默認渲染函數
|
|
233
|
+
* @private
|
|
234
|
+
*/
|
|
235
|
+
private makeDefaultRender(): (rowData: Record<string, any>) => string | HTMLElement | VNode {
|
|
236
|
+
// 根據欄位類型生成默認渲染函數
|
|
237
|
+
switch (this.type) {
|
|
238
|
+
case CTableColumnType.RowNumber:
|
|
239
|
+
return (rowData) => {
|
|
240
|
+
// 返回行號
|
|
241
|
+
const rowNumber = rowData['__rowNumber'] || 0; // 假設行號存儲在 rowData 中
|
|
242
|
+
return String(rowNumber || ''); // 行號從 1 開始
|
|
243
|
+
};
|
|
244
|
+
case CTableColumnType.Text:
|
|
245
|
+
return (rowData) => {
|
|
246
|
+
const value = eTool.getVal(rowData, this.dataName, this.defaultText);
|
|
247
|
+
// 轉換 newline 為 <br> 標籤
|
|
248
|
+
let newlineConverted = value;
|
|
249
|
+
if (this.useHtml && !_.isNil(value)) {
|
|
250
|
+
newlineConverted = String(value).replace(/\n/g, '<br/>');
|
|
251
|
+
}
|
|
252
|
+
return String(newlineConverted || '');
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
case CTableColumnType.Date:
|
|
256
|
+
if (!this.formatPattern) {
|
|
257
|
+
this.formatPattern = 'YYYY-MM-DD';
|
|
258
|
+
}
|
|
259
|
+
return (rowData) => {
|
|
260
|
+
const value = eTool.getVal(rowData, this.dataName, '');
|
|
261
|
+
if (_.isNil(value)) {
|
|
262
|
+
return '';
|
|
263
|
+
}
|
|
264
|
+
const dateObj = dayjs(value);
|
|
265
|
+
if (!dateObj.isValid()) {
|
|
266
|
+
return '';
|
|
267
|
+
}
|
|
268
|
+
return dateObj.format(this.formatPattern);
|
|
269
|
+
};
|
|
270
|
+
case CTableColumnType.DateTime:
|
|
271
|
+
if (!this.formatPattern) {
|
|
272
|
+
this.formatPattern = 'YYYY-MM-DD HH:mm'; // 默認日期時間格式
|
|
273
|
+
}
|
|
274
|
+
return (rowData) => {
|
|
275
|
+
const value = eTool.getVal(rowData, this.dataName);
|
|
276
|
+
// 使用 dayjs 格式化日期
|
|
277
|
+
if (_.isNil(value)) {
|
|
278
|
+
return '';
|
|
279
|
+
}
|
|
280
|
+
const dateObj = dayjs(value);
|
|
281
|
+
if (!dateObj.isValid()) {
|
|
282
|
+
return '';
|
|
283
|
+
}
|
|
284
|
+
return dateObj.format(this.formatPattern);
|
|
285
|
+
};
|
|
286
|
+
case CTableColumnType.DateRange:
|
|
287
|
+
if (!this.dataNameList || this.dataNameList.length !== 2) {
|
|
288
|
+
throw new Error('DateRange type requires dataNameList with exactly two items.');
|
|
289
|
+
}
|
|
290
|
+
if (!this.formatPattern) {
|
|
291
|
+
this.formatPattern = 'YYYY-MM-DD'; // 默認日期格式
|
|
292
|
+
}
|
|
293
|
+
return (rowData) => {
|
|
294
|
+
const startValue = eTool.getVal(rowData, this.dataNameList?.[0]);
|
|
295
|
+
const endValue = eTool.getVal(rowData, this.dataNameList?.[1]);
|
|
296
|
+
if (_.isNil(startValue) && _.isNil(endValue)) {
|
|
297
|
+
return '';
|
|
298
|
+
}
|
|
299
|
+
const showText = [];
|
|
300
|
+
if (!_.isNil(startValue)) {
|
|
301
|
+
showText.push(dayjs(startValue).format(this.formatPattern));
|
|
302
|
+
}
|
|
303
|
+
if (!_.isNil(endValue)) {
|
|
304
|
+
showText.push(dayjs(endValue).format(this.formatPattern));
|
|
305
|
+
}
|
|
306
|
+
return showText.join(' ~ ');
|
|
307
|
+
};
|
|
308
|
+
case CTableColumnType.Action:
|
|
309
|
+
this.useVNode = true;
|
|
310
|
+
return (rowData) => {
|
|
311
|
+
// 根據 action 集合中的操作類型生成按鈕 回傳使用 VNode
|
|
312
|
+
if (_.isNil(this.actionList) || _.isEmpty(this.actionList)) {
|
|
313
|
+
return '';
|
|
314
|
+
}
|
|
315
|
+
// 如果有 actionFilter,則過濾 actionList
|
|
316
|
+
let useActionList = this.actionList;
|
|
317
|
+
if (this.actionFilter) {
|
|
318
|
+
useActionList = this.actionList.filter(actionMetaItem => {
|
|
319
|
+
return this.actionFilter!(actionMetaItem, rowData);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
// 生成按鈕 VNode 列表
|
|
323
|
+
const btnNodeList = useActionList.map(actionMetaItem => {
|
|
324
|
+
const {cls: buttonCls, fontIcon, text} = this.buildActionButtonMeta(actionMetaItem);
|
|
325
|
+
const fontIconNode = h('i', {'class': fontIcon});
|
|
326
|
+
const textNode = h('span', {}, text);
|
|
327
|
+
// 使用 h 函數創建按鈕 VNode
|
|
328
|
+
return h('button', {
|
|
329
|
+
class: `${buttonCls} c-btn-${actionMetaItem.actionType}`,
|
|
330
|
+
onClick: (event: Event) => {
|
|
331
|
+
event.preventDefault();
|
|
332
|
+
event.stopPropagation();
|
|
333
|
+
this.handleAction(actionMetaItem, rowData);
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
}, [fontIconNode, textNode]);
|
|
337
|
+
});
|
|
338
|
+
// text 對齊class
|
|
339
|
+
let alignCls = '';
|
|
340
|
+
if (this.dataAlign === 'center') {
|
|
341
|
+
alignCls = 'justify-content-center';
|
|
342
|
+
} else if (this.dataAlign === 'right') {
|
|
343
|
+
alignCls = 'justify-content-end';
|
|
344
|
+
}
|
|
345
|
+
const clsArr = ['gap-1', alignCls];
|
|
346
|
+
// 返回包含所有按鈕的容器 VNode
|
|
347
|
+
return h('div', {
|
|
348
|
+
class: clsArr.join(' '),
|
|
349
|
+
}, btnNodeList);
|
|
350
|
+
}
|
|
351
|
+
case CTableColumnType.TextInput:
|
|
352
|
+
this.useVNode = true;
|
|
353
|
+
return (rowData) => {
|
|
354
|
+
// 返回輸入框 VNode
|
|
355
|
+
return h(CRowTextInput, {
|
|
356
|
+
rowData: rowData,
|
|
357
|
+
dataName: this.dataName ?? '',
|
|
358
|
+
'onUpdate:textInput': (val: string) => {
|
|
359
|
+
// 更新行數據中的對應屬性
|
|
360
|
+
if(this.dataName) {
|
|
361
|
+
_.set(rowData, this.dataName, val);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
})
|
|
365
|
+
};
|
|
366
|
+
case CTableColumnType.Checkbox:
|
|
367
|
+
this.useVNode = true;
|
|
368
|
+
return (rowData) => {
|
|
369
|
+
// 處理 vNodeAttrs 的 readModeFunc
|
|
370
|
+
if(_.isFunction(this.vNodeAttrs?.readModeFunc)) {
|
|
371
|
+
this.vNodeAttrs.readMode = this.vNodeAttrs.readModeFunc(rowData);
|
|
372
|
+
}
|
|
373
|
+
// 返回複選框 VNode
|
|
374
|
+
return h(CRowCheckBox, {
|
|
375
|
+
...(this.vNodeAttrs ?? {}),
|
|
376
|
+
align: this.dataAlign,
|
|
377
|
+
rowData: rowData,
|
|
378
|
+
'onUpdate:checked': (val: boolean, event: Event) => {
|
|
379
|
+
// 更新行數據中的 checked 屬性
|
|
380
|
+
(rowData as CheckableModel).checked = val;
|
|
381
|
+
// actionList 找出相對應的 actionMeta 並執行 handleAction
|
|
382
|
+
const actionMeta = this.actionList?.find(a => a.actionType === CTableColumnActionType.Check);
|
|
383
|
+
if(actionMeta) {
|
|
384
|
+
this.handleAction(actionMeta, rowData, event);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
default:
|
|
391
|
+
return (rowData) => '';
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* 根據操作類型生成按鈕的樣式和文字
|
|
397
|
+
* @param actionType 操作類型
|
|
398
|
+
* @returns {cls: string, text: string} 按鈕的樣式和文字
|
|
399
|
+
* @private
|
|
400
|
+
*/
|
|
401
|
+
private buildActionButtonMeta(actionMeta: CTableColumnActionMeta): { cls: string, fontIcon:string, text: string } {
|
|
402
|
+
const text = _.get(actionMeta, 'text', '');
|
|
403
|
+
const useClassName = _.get(actionMeta, 'cls', CTableColumn.defaultBtnClassMap[actionMeta.actionType] || '');
|
|
404
|
+
const useIcon = _.get(actionMeta, 'icon', CTableColumn.defaultIconMap[actionMeta.actionType] || '');
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
cls: useClassName,
|
|
408
|
+
fontIcon: useIcon,
|
|
409
|
+
text: text,
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
// ~ ----------------------------------------------------------
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* 分頁樣式配置介面
|
|
419
|
+
*/
|
|
420
|
+
export interface PaginationStyleConfig {
|
|
421
|
+
backgroundColor: string; // 背景顏色
|
|
422
|
+
textColor: string; // 文字顏色
|
|
423
|
+
borderColor: string; // 邊框顏色
|
|
424
|
+
hoverBackgroundColor: string; // hover 背景顏色
|
|
425
|
+
hoverTextColor: string; // hover 文字顏色
|
|
426
|
+
hoverBorderColor: string; // hover 邊框顏色
|
|
427
|
+
activeBackgroundColor: string; // active 背景顏色
|
|
428
|
+
activeTextColor: string; // active 文字顏色
|
|
429
|
+
activeBorderColor: string; // active 邊框顏色
|
|
430
|
+
previousIcon: string; // 上一頁圖示
|
|
431
|
+
nextIcon: string; // 下一頁圖示
|
|
432
|
+
firstIcon: string; // 第一頁圖示
|
|
433
|
+
lastIcon: string; // 最後一頁圖示
|
|
434
|
+
ellipsisIcon: string; // 省略號圖示
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export interface TableStyleConfig {
|
|
438
|
+
sortIconNone?: string; // 無指定排序圖示
|
|
439
|
+
sortIconAsc?: string; // 升序圖示
|
|
440
|
+
sortIconDesc?: string; // 降序圖示
|
|
441
|
+
scrollbarThumbColor?: string; // scrollbar thumb 顏色
|
|
442
|
+
scrollbarTrackColor?: string; // scrollbar track 顏色
|
|
443
|
+
scrollbarThumbHoverColor?: string; // scrollbar thumb hover 顏色
|
|
444
|
+
scrollbarHeight?: string; // scrollbar 高度
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* CTableConfig 類別用於配置表格的全域設定 包含以下
|
|
449
|
+
* - 分頁樣式設定
|
|
450
|
+
* - 每頁顯示選項
|
|
451
|
+
*/
|
|
452
|
+
export class CTableConfig {
|
|
453
|
+
|
|
454
|
+
private static tableStyleConfig: TableStyleConfig = {
|
|
455
|
+
sortIconNone: 'sort',
|
|
456
|
+
sortIconAsc: 'sort-up',
|
|
457
|
+
sortIconDesc: 'sort-down',
|
|
458
|
+
scrollbarThumbColor: '#E4B445',
|
|
459
|
+
scrollbarTrackColor: '#f1f1f1',
|
|
460
|
+
scrollbarThumbHoverColor: '#d4a33c',
|
|
461
|
+
scrollbarHeight: '8px',
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
static getTableStyleConfig(): TableStyleConfig {
|
|
465
|
+
return { ...CTableConfig.tableStyleConfig };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
static setTableStyleConfig(config: Partial<TableStyleConfig>): void {
|
|
469
|
+
CTableConfig.tableStyleConfig = {
|
|
470
|
+
...CTableConfig.tableStyleConfig,
|
|
471
|
+
...config
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// 預設分頁顏色樣式
|
|
476
|
+
private static defaultPaginationStyle: PaginationStyleConfig = {
|
|
477
|
+
backgroundColor: '#fff',
|
|
478
|
+
textColor: '#2e2e2e',
|
|
479
|
+
borderColor: '#E4B445',
|
|
480
|
+
hoverBackgroundColor: '#E4B445',
|
|
481
|
+
hoverTextColor: '#fff',
|
|
482
|
+
hoverBorderColor: '#d4a33c',
|
|
483
|
+
activeBackgroundColor: '#E4B445',
|
|
484
|
+
activeTextColor: '#fff',
|
|
485
|
+
activeBorderColor: '#E4B445',
|
|
486
|
+
previousIcon: 'angle-left',
|
|
487
|
+
nextIcon: 'ellipsis',
|
|
488
|
+
firstIcon: 'angle-double-left',
|
|
489
|
+
lastIcon: 'angle-double-right',
|
|
490
|
+
ellipsisIcon: 'angle-right',
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
// 當前使用的分頁樣式
|
|
494
|
+
private static currentPaginationStyle: PaginationStyleConfig = { ...CTableConfig.defaultPaginationStyle };
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* 設定全域分頁樣式
|
|
498
|
+
* @param config 分頁樣式配置
|
|
499
|
+
*/
|
|
500
|
+
static setPaginationStyle(config: Partial<PaginationStyleConfig>): void {
|
|
501
|
+
CTableConfig.currentPaginationStyle = {
|
|
502
|
+
...CTableConfig.currentPaginationStyle,
|
|
503
|
+
...config
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* 取得當前分頁樣式
|
|
509
|
+
*/
|
|
510
|
+
static getPaginationStyle(): PaginationStyleConfig {
|
|
511
|
+
return { ...CTableConfig.currentPaginationStyle };
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* 重置分頁樣式為預設值
|
|
516
|
+
*/
|
|
517
|
+
static resetPaginationStyle(): void {
|
|
518
|
+
CTableConfig.currentPaginationStyle = { ...CTableConfig.defaultPaginationStyle };
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* 產生分頁樣式的 CSS 變數物件
|
|
523
|
+
*/
|
|
524
|
+
static getPaginationStyleVars(): Record<string, string> {
|
|
525
|
+
const style = CTableConfig.currentPaginationStyle;
|
|
526
|
+
return {
|
|
527
|
+
'--c-pagination-bg-color': style.backgroundColor,
|
|
528
|
+
'--c-pagination-text-color': style.textColor,
|
|
529
|
+
'--c-pagination-border-color': style.borderColor,
|
|
530
|
+
'--c-pagination-hover-bg-color': style.hoverBackgroundColor,
|
|
531
|
+
'--c-pagination-hover-text-color': style.hoverTextColor,
|
|
532
|
+
'--c-pagination-hover-border-color': style.hoverBorderColor,
|
|
533
|
+
'--c-pagination-active-bg-color': style.activeBackgroundColor,
|
|
534
|
+
'--c-pagination-active-text-color': style.activeTextColor,
|
|
535
|
+
'--c-pagination-active-border-color': style.activeBorderColor,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* 產生表格樣式的 CSS 變數物件
|
|
541
|
+
*/
|
|
542
|
+
static getTableStyleVars(): Record<string, string> {
|
|
543
|
+
const style = CTableConfig.tableStyleConfig;
|
|
544
|
+
return {
|
|
545
|
+
'--c-scrollbar-thumb-color': style.scrollbarThumbColor || '#E4B445',
|
|
546
|
+
'--c-scrollbar-track-color': style.scrollbarTrackColor || '#f1f1f1',
|
|
547
|
+
'--c-scrollbar-thumb-hover-color': style.scrollbarThumbHoverColor || '#d4a33c',
|
|
548
|
+
'--c-scrollbar-height': style.scrollbarHeight || '8px',
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* 預設每頁顯示選項
|
|
554
|
+
*/
|
|
555
|
+
static pageSizeOptions = [10, 20, 50, 100];
|
|
556
|
+
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// 防止 Vite/rollup tree-shake 掉 type export
|
|
560
|
+
export const __CTableDefine__ = null as unknown as
|
|
561
|
+
CTableColumnType &
|
|
562
|
+
CTableColumnActionType &
|
|
563
|
+
CTableColumnActionParam &
|
|
564
|
+
CTableColumnActionMeta & CTableColumnDefine &
|
|
565
|
+
PaginationStyleConfig;
|
|
566
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {CTableColumn,} from "../components/CTableDefine";
|
|
3
|
+
|
|
4
|
+
export interface CTableTDProps {
|
|
5
|
+
columnItem: CTableColumn;
|
|
6
|
+
dataItem: Record<string, any>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const props = defineProps<CTableTDProps>();
|
|
10
|
+
const renderResult = props.columnItem.render(props.dataItem)
|
|
11
|
+
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<template v-if="props.columnItem.useHtml">
|
|
16
|
+
<div :class="props.columnItem.outerWrapperCls" v-html="renderResult"></div>
|
|
17
|
+
</template>
|
|
18
|
+
<template v-else-if="columnItem.useVNode">
|
|
19
|
+
<component :is="renderResult"/>
|
|
20
|
+
</template>
|
|
21
|
+
<template v-else>
|
|
22
|
+
<div :class="props.columnItem.outerWrapperCls">{{ renderResult }}</div>
|
|
23
|
+
</template>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<style scoped>
|
|
27
|
+
|
|
28
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject } from 'vue';
|
|
3
|
+
import { SESSION_STORE_KEY } from '../auth/keys';
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
need: string | string[];
|
|
7
|
+
}>();
|
|
8
|
+
|
|
9
|
+
// 注入由主專案提供的 sessionStore
|
|
10
|
+
const sessionStore = inject(SESSION_STORE_KEY);
|
|
11
|
+
|
|
12
|
+
// 檢查權限的邏輯
|
|
13
|
+
const hasPermission = () => {
|
|
14
|
+
if (!sessionStore) {
|
|
15
|
+
console.warn('[commons-vue] HasPermission: sessionStore not provided.');
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return sessionStore.hasPermission(props.need);
|
|
19
|
+
};
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<slot v-if="hasPermission()" />
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<style scoped>
|
|
27
|
+
|
|
28
|
+
</style>
|