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.
Files changed (112) hide show
  1. package/package.json +2 -1
  2. package/src/api/ApiService.ts +869 -0
  3. package/src/auth/AuthorizationService.ts +138 -0
  4. package/src/auth/PermissionDescriptor.ts +99 -0
  5. package/src/auth/keys.ts +5 -0
  6. package/src/components/CAlert.vue +188 -0
  7. package/src/components/CAlertDefine.ts +20 -0
  8. package/src/components/CBSToast.vue +119 -0
  9. package/src/components/CGlobalSpinner.vue +84 -0
  10. package/src/components/CImage.vue +67 -0
  11. package/src/components/CRowCheckBox.vue +75 -0
  12. package/src/components/CRowTextInput.vue +27 -0
  13. package/src/components/CTable.vue +524 -0
  14. package/src/components/CTableDefine.ts +566 -0
  15. package/src/components/CTableTD.vue +28 -0
  16. package/src/components/HasPermission.vue +28 -0
  17. package/src/components/form/CChangePasswordFormField.vue +146 -0
  18. package/src/components/form/CCheckBoxFormField.vue +91 -0
  19. package/src/components/form/CCheckBoxPlatFormField.vue +94 -0
  20. package/src/components/form/CDateFormField.vue +149 -0
  21. package/src/components/form/CDateQueryField.vue +111 -0
  22. package/src/components/form/CDateRangeFormField.vue +138 -0
  23. package/src/components/form/CFilePickerFormField.vue +471 -0
  24. package/src/components/form/CRadioFormField.vue +62 -0
  25. package/src/components/form/CRadioPlatFormField.vue +67 -0
  26. package/src/components/form/CSelectFormField.vue +175 -0
  27. package/src/components/form/CTextAreaFormField.vue +84 -0
  28. package/src/components/form/CTextInputFormField.vue +99 -0
  29. package/src/components/form/CTinyMCEEditorFormField.vue +99 -0
  30. package/src/components/form/SCTextInputFormField.vue +129 -0
  31. package/src/composables/useCheckBoxFormField.ts +126 -0
  32. package/src/composables/useRadioFormField.ts +106 -0
  33. package/src/directive/CBootstrapDirective.ts +83 -0
  34. package/src/directive/CDateFormatterDirective.ts +37 -0
  35. package/src/directive/CFTurnstileDirective.ts +46 -0
  36. package/src/directive/CFormDirective.ts +57 -0
  37. package/src/directive/PermissionDirective.ts +102 -0
  38. package/src/env.d.ts +19 -0
  39. package/src/index.ts +83 -0
  40. package/src/model/BSFieldStyleConfig.ts +349 -0
  41. package/src/model/BaseDictionary.ts +86 -0
  42. package/src/model/BaseFormDataModel.ts +623 -0
  43. package/src/model/BaseListViewModel.ts +392 -0
  44. package/src/model/CBSModalViewModel.ts +91 -0
  45. package/src/model/CFileDataModel.ts +181 -0
  46. package/src/model/CImageViewModel.ts +34 -0
  47. package/src/model/CMenuItem.ts +199 -0
  48. package/src/model/EmailReceiverDataModel.ts +149 -0
  49. package/src/model/EmptyDataModel.ts +25 -0
  50. package/src/model/FormOptions.ts +112 -0
  51. package/src/model/LoginDataModel.ts +51 -0
  52. package/src/model/PasswordDataModel.ts +70 -0
  53. package/src/model/QueryParameter.ts +310 -0
  54. package/src/model/SessionUser.ts +110 -0
  55. package/src/model/ShowMessageDataModel.ts +69 -0
  56. package/src/model/TokenUser.ts +157 -0
  57. package/src/stores/FormDataStore.ts +73 -0
  58. package/src/stores/ViewStore.ts +701 -0
  59. package/src/stores/VueSessionStoreInstaller.ts +22 -0
  60. package/src/types/turnstile.d.ts +8 -0
  61. package/src/utils/CToolUtils.ts +133 -0
  62. package/dist/api/ApiService.d.ts +0 -233
  63. package/dist/auth/AuthorizationService.d.ts +0 -56
  64. package/dist/auth/PermissionDescriptor.d.ts +0 -37
  65. package/dist/components/CAlert.vue.d.ts +0 -17
  66. package/dist/components/CAlertDefine.d.ts +0 -14
  67. package/dist/components/CBSToast.vue.d.ts +0 -6
  68. package/dist/components/CGlobalSpinner.vue.d.ts +0 -13
  69. package/dist/components/CRowCheckBox.vue.d.ts +0 -14
  70. package/dist/components/CRowTextInput.vue.d.ts +0 -10
  71. package/dist/components/CTable.vue.d.ts +0 -24
  72. package/dist/components/CTableDefine.d.ts +0 -201
  73. package/dist/components/CTableTD.vue.d.ts +0 -7
  74. package/dist/components/form/CChangePasswordFormField.vue.d.ts +0 -14
  75. package/dist/components/form/CCheckBoxFormField.vue.d.ts +0 -30
  76. package/dist/components/form/CDateFormField.vue.d.ts +0 -17
  77. package/dist/components/form/CDateQueryField.vue.d.ts +0 -16
  78. package/dist/components/form/CDateRangeFormField.vue.d.ts +0 -17
  79. package/dist/components/form/CFilePickerFormField.vue.d.ts +0 -28
  80. package/dist/components/form/CRadioFormField.vue.d.ts +0 -30
  81. package/dist/components/form/CSelectFormField.vue.d.ts +0 -18
  82. package/dist/components/form/CTextAreaFormField.vue.d.ts +0 -16
  83. package/dist/components/form/CTextInputFormField.vue.d.ts +0 -22
  84. package/dist/directive/CBootstrapDirective.d.ts +0 -17
  85. package/dist/directive/CDateFormatterDirective.d.ts +0 -10
  86. package/dist/directive/CFTurnstileDirective.d.ts +0 -15
  87. package/dist/directive/CFormDirective.d.ts +0 -9
  88. package/dist/directive/PermissionDirective.d.ts +0 -15
  89. package/dist/index.cjs.js +0 -19103
  90. package/dist/index.d.ts +0 -45
  91. package/dist/index.es.js +0 -19086
  92. package/dist/model/BSFieldStyleConfig.d.ts +0 -121
  93. package/dist/model/BaseDictionary.d.ts +0 -34
  94. package/dist/model/BaseFormDataModel.d.ts +0 -199
  95. package/dist/model/BaseListViewModel.d.ts +0 -165
  96. package/dist/model/CBSModalViewModel.d.ts +0 -44
  97. package/dist/model/CFileDataModel.d.ts +0 -74
  98. package/dist/model/CImageViewModel.d.ts +0 -8
  99. package/dist/model/CMenuItem.d.ts +0 -86
  100. package/dist/model/EmailReceiverDataModel.d.ts +0 -57
  101. package/dist/model/EmptyDataModel.d.ts +0 -7
  102. package/dist/model/FormOptions.d.ts +0 -60
  103. package/dist/model/LoginDataModel.d.ts +0 -12
  104. package/dist/model/PasswordDataModel.d.ts +0 -15
  105. package/dist/model/QueryParameter.d.ts +0 -92
  106. package/dist/model/SessionUser.d.ts +0 -45
  107. package/dist/model/ShowMessageDataModel.d.ts +0 -44
  108. package/dist/model/TokenUser.d.ts +0 -50
  109. package/dist/stores/FormDataStore.d.ts +0 -31
  110. package/dist/stores/ViewStore.d.ts +0 -349
  111. package/dist/style.css +0 -223
  112. 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>