shared-ritm 1.2.46 → 1.2.48
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/README.md +103 -103
- package/dist/index.css +1 -1
- package/dist/shared-ritm.es.js +199855 -72176
- package/dist/shared-ritm.umd.js +97 -42
- package/dist/types/api/services/ControlsService.d.ts +1 -0
- package/dist/types/api/types/Api_Controls.d.ts +12 -2
- package/dist/types/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/api/services/ControlsService.ts +40 -36
- package/src/api/services/FileService.ts +15 -15
- package/src/api/services/GanttService.ts +17 -17
- package/src/api/services/MetricsService.ts +109 -109
- package/src/api/services/RepairsService.ts +100 -100
- package/src/api/settings/ApiService.ts +128 -128
- package/src/api/types/Api_Controls.ts +12 -2
- package/src/api/types/Api_Files.ts +1 -1
- package/src/api/types/Api_Projects.ts +55 -55
- package/src/api/types/Api_Repairs.ts +93 -93
- package/src/common/app-button/AppButton.vue +173 -173
- package/src/common/app-checkbox/AppCheckbox.vue +26 -26
- package/src/common/app-dialogs/AppConfirmDialog.vue +99 -99
- package/src/common/app-dropdown/AppDropdown.vue +31 -31
- package/src/common/app-input/AppInput.vue +147 -147
- package/src/common/app-loader/index.vue +43 -43
- package/src/common/app-page-layout/AppPageLayout.vue +122 -122
- package/src/common/app-select/AppSelect.vue +157 -157
- package/src/common/app-sidebar/AppSidebar.vue +168 -168
- package/src/common/app-sidebar/components/SidebarMenu.vue +37 -37
- package/src/common/app-sidebar/components/SidebarMenuItem.vue +148 -148
- package/src/common/app-table/AppTable.vue +211 -211
- package/src/common/app-table/AppTableLayout.vue +93 -93
- package/src/common/app-table/components/ModalSelect.vue +215 -0
- package/src/common/app-table/components/TableModal.vue +26 -53
- package/src/common/app-table/components/TablePagination.vue +151 -151
- package/src/common/app-table/components/TableSearch.vue +78 -78
- package/src/common/app-table/controllers/useBaseTable.ts +43 -43
- package/src/common/app-table/controllers/useColumnSelector.ts +38 -38
- package/src/common/app-table/controllers/useTableModel.ts +93 -93
- package/src/common/app-toggle/AppToggle.vue +23 -23
- package/src/common/app-wrapper/AppWrapper.vue +28 -28
- package/src/global.d.ts +1 -1
- package/src/icons/components/arrow-down-icon.vue +25 -25
- package/src/icons/components/arrow-frame-icon.vue +19 -19
- package/src/icons/components/arrow-square.vue +22 -22
- package/src/icons/components/table-filter-icon.vue +30 -30
- package/src/icons/dialogs/RemoveIcon.vue +12 -12
- package/src/icons/dialogs/SafetyIcon.vue +12 -12
- package/src/icons/header/flashIcon.vue +24 -24
- package/src/icons/header/searchStatusIcon.vue +24 -24
- package/src/icons/header/smallCapsIcon.vue +34 -34
- package/src/icons/sidebar/assign-module-icon.vue +36 -36
- package/src/icons/sidebar/instrument-history-icon.vue +32 -32
- package/src/icons/sidebar/instrument-order-icon.vue +38 -38
- package/src/icons/sidebar/instrument-work-zone-icon.vue +18 -18
- package/src/icons/sidebar/instruments-icon.vue +45 -45
- package/src/icons/sidebar/logo-icon.vue +15 -15
- package/src/icons/sidebar/logout-icon.vue +13 -13
- package/src/icons/sidebar/modules-icon.vue +16 -16
- package/src/icons/sidebar/notifications-icon.vue +24 -24
- package/src/icons/sidebar/order-icon.vue +44 -44
- package/src/icons/sidebar/pass-icon.vue +38 -38
- package/src/icons/sidebar/positions-icon.vue +42 -42
- package/src/icons/sidebar/preorder-icon.vue +19 -19
- package/src/icons/sidebar/projects-icon.vue +31 -31
- package/src/icons/sidebar/repair-object-icon.vue +18 -18
- package/src/icons/sidebar/repairs-icon.vue +20 -20
- package/src/icons/sidebar/roles-icon.vue +26 -26
- package/src/icons/sidebar/status-history-icon.vue +24 -24
- package/src/icons/sidebar/tasks-icon.vue +28 -28
- package/src/icons/sidebar/tasks_tasks-icon.vue +39 -39
- package/src/icons/sidebar/tasks_today-icon.vue +27 -27
- package/src/icons/sidebar/teams-icon.vue +32 -32
- package/src/icons/sidebar/user-icon.vue +18 -18
- package/src/icons/sidebar/users-icon.vue +46 -46
- package/src/icons/sidebar/videosources-icon.vue +19 -19
- package/src/icons/sidebar/videowall-icon.vue +13 -13
- package/src/icons/sidebar/videozones-icon.vue +21 -21
- package/src/icons/sidebar/warehouses-icon.vue +43 -43
- package/src/icons/sidebar/workshop-icon.vue +100 -100
- package/src/icons/sidebar/workzones-icon.vue +22 -22
- package/src/icons/task/attention-icon.vue +13 -13
- package/src/icons/task/clock-icon.vue +10 -10
- package/src/icons/task/delete-icon.vue +10 -10
- package/src/icons/task/fire-icon.vue +16 -16
- package/src/index.ts +2 -0
- package/src/quasar-user-options.ts +17 -17
- package/src/router/index.ts +10 -10
- package/src/shared/styles/general.css +96 -96
- package/src/shims-vue.d.ts +5 -5
- package/src/utils/confirm.ts +12 -12
- package/src/utils/notification.ts +9 -9
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { defineProps, defineEmits, ref, watch } from 'vue'
|
|
3
|
-
|
|
4
|
-
const props = defineProps<{
|
|
5
|
-
modelValue: string
|
|
6
|
-
placeholder: string
|
|
7
|
-
}>()
|
|
8
|
-
|
|
9
|
-
const emit = defineEmits<{
|
|
10
|
-
(e: 'update:modelValue', value: string): void
|
|
11
|
-
(e: 'search', value: string): void
|
|
12
|
-
}>()
|
|
13
|
-
|
|
14
|
-
const input = ref(props.modelValue)
|
|
15
|
-
let debounceTimer: ReturnType<typeof setTimeout>
|
|
16
|
-
|
|
17
|
-
watch(
|
|
18
|
-
() => props.modelValue,
|
|
19
|
-
val => {
|
|
20
|
-
input.value = val
|
|
21
|
-
},
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
watch(input, val => {
|
|
25
|
-
emit('update:modelValue', val)
|
|
26
|
-
clearTimeout(debounceTimer)
|
|
27
|
-
debounceTimer = setTimeout(() => {
|
|
28
|
-
emit('search', val)
|
|
29
|
-
}, 400)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
const clearInput = () => {
|
|
33
|
-
input.value = ''
|
|
34
|
-
emit('search', '')
|
|
35
|
-
}
|
|
36
|
-
</script>
|
|
37
|
-
|
|
38
|
-
<template>
|
|
39
|
-
<q-input v-model="input" :placeholder="placeholder" dense outlined>
|
|
40
|
-
<template v-if="input" #append>
|
|
41
|
-
<q-icon name="✕" class="cursor-pointer" @click.stop="clearInput" />
|
|
42
|
-
</template>
|
|
43
|
-
</q-input>
|
|
44
|
-
</template>
|
|
45
|
-
|
|
46
|
-
<style scoped lang="scss">
|
|
47
|
-
.q-field {
|
|
48
|
-
background: #fff;
|
|
49
|
-
border-radius: 4px;
|
|
50
|
-
outline: none;
|
|
51
|
-
border-color: #f2f7fb;
|
|
52
|
-
|
|
53
|
-
::v-deep(.q-field__control) {
|
|
54
|
-
height: 52px;
|
|
55
|
-
display: flex;
|
|
56
|
-
align-items: center;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
::v-deep(.q-field__native) {
|
|
60
|
-
font-family: NunitoSansFont, sans-serif;
|
|
61
|
-
color: #1d425d;
|
|
62
|
-
padding-left: 8px;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
::v-deep(.q-field__native::placeholder) {
|
|
66
|
-
color: rgba(184, 184, 184, 0.87);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
::v-deep(.q-icon.cursor-pointer) {
|
|
70
|
-
color: #1d425d;
|
|
71
|
-
font-size: 20px;
|
|
72
|
-
line-height: 15px;
|
|
73
|
-
}
|
|
74
|
-
::v-deep(.q-field__control:hover:before) {
|
|
75
|
-
border-color: #3f8cff;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
</style>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { defineProps, defineEmits, ref, watch } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
modelValue: string
|
|
6
|
+
placeholder: string
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
const emit = defineEmits<{
|
|
10
|
+
(e: 'update:modelValue', value: string): void
|
|
11
|
+
(e: 'search', value: string): void
|
|
12
|
+
}>()
|
|
13
|
+
|
|
14
|
+
const input = ref(props.modelValue)
|
|
15
|
+
let debounceTimer: ReturnType<typeof setTimeout>
|
|
16
|
+
|
|
17
|
+
watch(
|
|
18
|
+
() => props.modelValue,
|
|
19
|
+
val => {
|
|
20
|
+
input.value = val
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
watch(input, val => {
|
|
25
|
+
emit('update:modelValue', val)
|
|
26
|
+
clearTimeout(debounceTimer)
|
|
27
|
+
debounceTimer = setTimeout(() => {
|
|
28
|
+
emit('search', val)
|
|
29
|
+
}, 400)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const clearInput = () => {
|
|
33
|
+
input.value = ''
|
|
34
|
+
emit('search', '')
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<q-input v-model="input" :placeholder="placeholder" dense outlined>
|
|
40
|
+
<template v-if="input" #append>
|
|
41
|
+
<q-icon name="✕" class="cursor-pointer" @click.stop="clearInput" />
|
|
42
|
+
</template>
|
|
43
|
+
</q-input>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<style scoped lang="scss">
|
|
47
|
+
.q-field {
|
|
48
|
+
background: #fff;
|
|
49
|
+
border-radius: 4px;
|
|
50
|
+
outline: none;
|
|
51
|
+
border-color: #f2f7fb;
|
|
52
|
+
|
|
53
|
+
::v-deep(.q-field__control) {
|
|
54
|
+
height: 52px;
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
::v-deep(.q-field__native) {
|
|
60
|
+
font-family: NunitoSansFont, sans-serif;
|
|
61
|
+
color: #1d425d;
|
|
62
|
+
padding-left: 8px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
::v-deep(.q-field__native::placeholder) {
|
|
66
|
+
color: rgba(184, 184, 184, 0.87);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
::v-deep(.q-icon.cursor-pointer) {
|
|
70
|
+
color: #1d425d;
|
|
71
|
+
font-size: 20px;
|
|
72
|
+
line-height: 15px;
|
|
73
|
+
}
|
|
74
|
+
::v-deep(.q-field__control:hover:before) {
|
|
75
|
+
border-color: #3f8cff;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { ref, watch, onMounted } from 'vue'
|
|
2
|
-
|
|
3
|
-
export function useBaseTable(model: any) {
|
|
4
|
-
const search = ref('')
|
|
5
|
-
const currentPage = ref(1)
|
|
6
|
-
const loading = ref(false)
|
|
7
|
-
|
|
8
|
-
const loadTable = async (searchVal = '', pageVal = 1) => {
|
|
9
|
-
loading.value = true
|
|
10
|
-
try {
|
|
11
|
-
await model.initialize({ search: searchVal, page: pageVal })
|
|
12
|
-
} finally {
|
|
13
|
-
loading.value = false
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const handleSearch = async (val: string) => {
|
|
18
|
-
currentPage.value = 1
|
|
19
|
-
await loadTable(val, currentPage.value)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const handlePageChange = async (page: number) => {
|
|
23
|
-
currentPage.value = page
|
|
24
|
-
await loadTable(search.value, page)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
watch(
|
|
28
|
-
() => model.columnFilters.value,
|
|
29
|
-
() => loadTable(search.value, currentPage.value),
|
|
30
|
-
{ deep: true },
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
onMounted(() => loadTable())
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
search,
|
|
37
|
-
currentPage,
|
|
38
|
-
loading,
|
|
39
|
-
handleSearch,
|
|
40
|
-
handlePageChange,
|
|
41
|
-
loadTable,
|
|
42
|
-
}
|
|
43
|
-
}
|
|
1
|
+
import { ref, watch, onMounted } from 'vue'
|
|
2
|
+
|
|
3
|
+
export function useBaseTable(model: any) {
|
|
4
|
+
const search = ref('')
|
|
5
|
+
const currentPage = ref(1)
|
|
6
|
+
const loading = ref(false)
|
|
7
|
+
|
|
8
|
+
const loadTable = async (searchVal = '', pageVal = 1) => {
|
|
9
|
+
loading.value = true
|
|
10
|
+
try {
|
|
11
|
+
await model.initialize({ search: searchVal, page: pageVal })
|
|
12
|
+
} finally {
|
|
13
|
+
loading.value = false
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const handleSearch = async (val: string) => {
|
|
18
|
+
currentPage.value = 1
|
|
19
|
+
await loadTable(val, currentPage.value)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const handlePageChange = async (page: number) => {
|
|
23
|
+
currentPage.value = page
|
|
24
|
+
await loadTable(search.value, page)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
watch(
|
|
28
|
+
() => model.columnFilters.value,
|
|
29
|
+
() => loadTable(search.value, currentPage.value),
|
|
30
|
+
{ deep: true },
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
onMounted(() => loadTable())
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
search,
|
|
37
|
+
currentPage,
|
|
38
|
+
loading,
|
|
39
|
+
handleSearch,
|
|
40
|
+
handlePageChange,
|
|
41
|
+
loadTable,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { computed, ref } from 'vue'
|
|
2
|
-
|
|
3
|
-
export interface ColumnConfig {
|
|
4
|
-
name: string
|
|
5
|
-
label: string
|
|
6
|
-
[key: string]: any
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function useColumnSelector(allColumns: ColumnConfig[], fixedColumnNames: string[] = []) {
|
|
10
|
-
const selectedColumnNames = ref<string[]>([...new Set([...fixedColumnNames, ...allColumns.map(col => col.name)])])
|
|
11
|
-
|
|
12
|
-
const visibleColumns = computed(() => allColumns.filter(col => selectedColumnNames.value.includes(col.name)))
|
|
13
|
-
|
|
14
|
-
function openColumnSelectorDialog($q: any) {
|
|
15
|
-
$q.dialog({
|
|
16
|
-
title: 'Выберите колонки',
|
|
17
|
-
options: {
|
|
18
|
-
type: 'checkbox',
|
|
19
|
-
model: [...selectedColumnNames.value],
|
|
20
|
-
items: allColumns.map(col => ({
|
|
21
|
-
label: col.label,
|
|
22
|
-
value: col.name,
|
|
23
|
-
disable: fixedColumnNames.includes(col.name),
|
|
24
|
-
})),
|
|
25
|
-
},
|
|
26
|
-
cancel: true,
|
|
27
|
-
persistent: true,
|
|
28
|
-
}).onOk((val: string[]) => {
|
|
29
|
-
selectedColumnNames.value = Array.from(new Set([...val, ...fixedColumnNames]))
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
selectedColumnNames,
|
|
35
|
-
visibleColumns,
|
|
36
|
-
openColumnSelectorDialog,
|
|
37
|
-
}
|
|
38
|
-
}
|
|
1
|
+
import { computed, ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
export interface ColumnConfig {
|
|
4
|
+
name: string
|
|
5
|
+
label: string
|
|
6
|
+
[key: string]: any
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function useColumnSelector(allColumns: ColumnConfig[], fixedColumnNames: string[] = []) {
|
|
10
|
+
const selectedColumnNames = ref<string[]>([...new Set([...fixedColumnNames, ...allColumns.map(col => col.name)])])
|
|
11
|
+
|
|
12
|
+
const visibleColumns = computed(() => allColumns.filter(col => selectedColumnNames.value.includes(col.name)))
|
|
13
|
+
|
|
14
|
+
function openColumnSelectorDialog($q: any) {
|
|
15
|
+
$q.dialog({
|
|
16
|
+
title: 'Выберите колонки',
|
|
17
|
+
options: {
|
|
18
|
+
type: 'checkbox',
|
|
19
|
+
model: [...selectedColumnNames.value],
|
|
20
|
+
items: allColumns.map(col => ({
|
|
21
|
+
label: col.label,
|
|
22
|
+
value: col.name,
|
|
23
|
+
disable: fixedColumnNames.includes(col.name),
|
|
24
|
+
})),
|
|
25
|
+
},
|
|
26
|
+
cancel: true,
|
|
27
|
+
persistent: true,
|
|
28
|
+
}).onOk((val: string[]) => {
|
|
29
|
+
selectedColumnNames.value = Array.from(new Set([...val, ...fixedColumnNames]))
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
selectedColumnNames,
|
|
35
|
+
visibleColumns,
|
|
36
|
+
openColumnSelectorDialog,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
import { Ref, computed, ref } from 'vue'
|
|
2
|
-
|
|
3
|
-
export interface TableColumn {
|
|
4
|
-
name: string
|
|
5
|
-
label: string
|
|
6
|
-
style?: string
|
|
7
|
-
headerStyle?: string
|
|
8
|
-
field: string | ((row: any) => any)
|
|
9
|
-
sortable?: boolean
|
|
10
|
-
filterType: 'single' | 'multi' | null
|
|
11
|
-
align?: 'left' | 'center' | 'right'
|
|
12
|
-
badge?: {
|
|
13
|
-
true?: string
|
|
14
|
-
false?: string
|
|
15
|
-
colorTrue?: string
|
|
16
|
-
colorFalse?: string
|
|
17
|
-
}
|
|
18
|
-
format?: (val: any) => any
|
|
19
|
-
html?: boolean
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface FilterOption {
|
|
23
|
-
id: string
|
|
24
|
-
name: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface TableModel<T = any> {
|
|
28
|
-
columns: TableColumn[]
|
|
29
|
-
rows: T[] | Ref<T[]>
|
|
30
|
-
filtersOptions?: Ref<Record<string, FilterOption[]>>
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const useTableModel = <T = any>(model: TableModel<T>) => {
|
|
34
|
-
const columnFilters = ref<Record<string, string | string[] | undefined>>({})
|
|
35
|
-
const filterMenus = ref<Record<string, boolean>>({})
|
|
36
|
-
|
|
37
|
-
model.columns.forEach(({ name, filterType }) => {
|
|
38
|
-
if (filterType) {
|
|
39
|
-
columnFilters.value[name] = filterType === 'multi' ? [] : undefined
|
|
40
|
-
filterMenus.value[name] = false
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const resolvedRows = computed(() => (Array.isArray(model.rows) ? model.rows : model.rows.value))
|
|
45
|
-
|
|
46
|
-
const toggleFilterValue = (colName: string, value: string) => {
|
|
47
|
-
const col = model.columns.find(c => c.name === colName)
|
|
48
|
-
if (col?.filterType === 'multi') {
|
|
49
|
-
const current = columnFilters.value[colName] as string[]
|
|
50
|
-
const index = current.indexOf(value)
|
|
51
|
-
index > -1 ? current.splice(index, 1) : current.push(value)
|
|
52
|
-
columnFilters.value[colName] = [...current]
|
|
53
|
-
} else {
|
|
54
|
-
columnFilters.value[colName] = value
|
|
55
|
-
filterMenus.value[colName] = false
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const selectedFilters = computed(() => {
|
|
60
|
-
const result: Record<string, string[]> = {}
|
|
61
|
-
for (const col of model.columns) {
|
|
62
|
-
const filter = columnFilters.value[col.name]
|
|
63
|
-
const options = model.filtersOptions?.value[col.name] || []
|
|
64
|
-
|
|
65
|
-
if (filter) {
|
|
66
|
-
result[col.name] = Array.isArray(filter)
|
|
67
|
-
? options.filter(opt => filter.includes(opt.name)).map(opt => opt.id)
|
|
68
|
-
: options.filter(opt => opt.name === filter).map(opt => opt.id)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return result
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
const clearFilter = (colName: string) => {
|
|
75
|
-
const col = model.columns.find(c => c.name === colName)
|
|
76
|
-
columnFilters.value[colName] = col?.filterType === 'multi' ? [] : undefined
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const openFilterMenu = (colName: string, isOpen: boolean) => {
|
|
80
|
-
filterMenus.value[colName] = isOpen
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
rows: resolvedRows,
|
|
85
|
-
columns: computed(() => model.columns),
|
|
86
|
-
columnFilters,
|
|
87
|
-
filterMenus,
|
|
88
|
-
toggleFilterValue,
|
|
89
|
-
clearFilter,
|
|
90
|
-
openFilterMenu,
|
|
91
|
-
selectedFilters,
|
|
92
|
-
}
|
|
93
|
-
}
|
|
1
|
+
import { Ref, computed, ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
export interface TableColumn {
|
|
4
|
+
name: string
|
|
5
|
+
label: string
|
|
6
|
+
style?: string
|
|
7
|
+
headerStyle?: string
|
|
8
|
+
field: string | ((row: any) => any)
|
|
9
|
+
sortable?: boolean
|
|
10
|
+
filterType: 'single' | 'multi' | null
|
|
11
|
+
align?: 'left' | 'center' | 'right'
|
|
12
|
+
badge?: {
|
|
13
|
+
true?: string
|
|
14
|
+
false?: string
|
|
15
|
+
colorTrue?: string
|
|
16
|
+
colorFalse?: string
|
|
17
|
+
}
|
|
18
|
+
format?: (val: any) => any
|
|
19
|
+
html?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface FilterOption {
|
|
23
|
+
id: string
|
|
24
|
+
name: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface TableModel<T = any> {
|
|
28
|
+
columns: TableColumn[]
|
|
29
|
+
rows: T[] | Ref<T[]>
|
|
30
|
+
filtersOptions?: Ref<Record<string, FilterOption[]>>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const useTableModel = <T = any>(model: TableModel<T>) => {
|
|
34
|
+
const columnFilters = ref<Record<string, string | string[] | undefined>>({})
|
|
35
|
+
const filterMenus = ref<Record<string, boolean>>({})
|
|
36
|
+
|
|
37
|
+
model.columns.forEach(({ name, filterType }) => {
|
|
38
|
+
if (filterType) {
|
|
39
|
+
columnFilters.value[name] = filterType === 'multi' ? [] : undefined
|
|
40
|
+
filterMenus.value[name] = false
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const resolvedRows = computed(() => (Array.isArray(model.rows) ? model.rows : model.rows.value))
|
|
45
|
+
|
|
46
|
+
const toggleFilterValue = (colName: string, value: string) => {
|
|
47
|
+
const col = model.columns.find(c => c.name === colName)
|
|
48
|
+
if (col?.filterType === 'multi') {
|
|
49
|
+
const current = columnFilters.value[colName] as string[]
|
|
50
|
+
const index = current.indexOf(value)
|
|
51
|
+
index > -1 ? current.splice(index, 1) : current.push(value)
|
|
52
|
+
columnFilters.value[colName] = [...current]
|
|
53
|
+
} else {
|
|
54
|
+
columnFilters.value[colName] = value
|
|
55
|
+
filterMenus.value[colName] = false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const selectedFilters = computed(() => {
|
|
60
|
+
const result: Record<string, string[]> = {}
|
|
61
|
+
for (const col of model.columns) {
|
|
62
|
+
const filter = columnFilters.value[col.name]
|
|
63
|
+
const options = model.filtersOptions?.value[col.name] || []
|
|
64
|
+
|
|
65
|
+
if (filter) {
|
|
66
|
+
result[col.name] = Array.isArray(filter)
|
|
67
|
+
? options.filter(opt => filter.includes(opt.name)).map(opt => opt.id)
|
|
68
|
+
: options.filter(opt => opt.name === filter).map(opt => opt.id)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return result
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const clearFilter = (colName: string) => {
|
|
75
|
+
const col = model.columns.find(c => c.name === colName)
|
|
76
|
+
columnFilters.value[colName] = col?.filterType === 'multi' ? [] : undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const openFilterMenu = (colName: string, isOpen: boolean) => {
|
|
80
|
+
filterMenus.value[colName] = isOpen
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
rows: resolvedRows,
|
|
85
|
+
columns: computed(() => model.columns),
|
|
86
|
+
columnFilters,
|
|
87
|
+
filterMenus,
|
|
88
|
+
toggleFilterValue,
|
|
89
|
+
clearFilter,
|
|
90
|
+
openFilterMenu,
|
|
91
|
+
selectedFilters,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<q-toggle v-model="value" />
|
|
4
|
-
</div>
|
|
5
|
-
</template>
|
|
6
|
-
|
|
7
|
-
<script setup lang="ts">
|
|
8
|
-
import { computed } from 'vue'
|
|
9
|
-
|
|
10
|
-
interface Props {
|
|
11
|
-
modelValue: any
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const props = defineProps<Props>()
|
|
15
|
-
const emit = defineEmits(['update:modelValue', 'number'])
|
|
16
|
-
|
|
17
|
-
const value = computed({
|
|
18
|
-
get: () => props.modelValue,
|
|
19
|
-
set: (newValue: any) => emit('update:modelValue', newValue),
|
|
20
|
-
})
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<style module lang="scss"></style>
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<q-toggle v-model="value" />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
modelValue: any
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = defineProps<Props>()
|
|
15
|
+
const emit = defineEmits(['update:modelValue', 'number'])
|
|
16
|
+
|
|
17
|
+
const value = computed({
|
|
18
|
+
get: () => props.modelValue,
|
|
19
|
+
set: (newValue: any) => emit('update:modelValue', newValue),
|
|
20
|
+
})
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<style module lang="scss"></style>
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="app-wrapper">
|
|
3
|
-
<slot />
|
|
4
|
-
</div>
|
|
5
|
-
</template>
|
|
6
|
-
<script setup lang="ts"></script>
|
|
7
|
-
<style scoped lang="scss">
|
|
8
|
-
.app-wrapper {
|
|
9
|
-
width: 100%;
|
|
10
|
-
height: 100%;
|
|
11
|
-
margin: 20px 0 12px 0;
|
|
12
|
-
padding: 30px;
|
|
13
|
-
border-radius: 8px;
|
|
14
|
-
background: #fff;
|
|
15
|
-
outline: 4px solid #598dd5;
|
|
16
|
-
outline-offset: -3px;
|
|
17
|
-
box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25), 0px 4px 4px 0px #c4d7f1 inset,
|
|
18
|
-
0px 0px 6.4px 3px rgba(31, 82, 159, 0.5), 0px 6px 58px 0px rgba(0, 49, 122, 0.1);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
@media (max-width: 1440px) {
|
|
22
|
-
.app-wrapper {
|
|
23
|
-
margin: 10px 0;
|
|
24
|
-
padding: 14px;
|
|
25
|
-
border-radius: 8px;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<div class="app-wrapper">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
<script setup lang="ts"></script>
|
|
7
|
+
<style scoped lang="scss">
|
|
8
|
+
.app-wrapper {
|
|
9
|
+
width: 100%;
|
|
10
|
+
height: 100%;
|
|
11
|
+
margin: 20px 0 12px 0;
|
|
12
|
+
padding: 30px;
|
|
13
|
+
border-radius: 8px;
|
|
14
|
+
background: #fff;
|
|
15
|
+
outline: 4px solid #598dd5;
|
|
16
|
+
outline-offset: -3px;
|
|
17
|
+
box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25), 0px 4px 4px 0px #c4d7f1 inset,
|
|
18
|
+
0px 0px 6.4px 3px rgba(31, 82, 159, 0.5), 0px 6px 58px 0px rgba(0, 49, 122, 0.1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@media (max-width: 1440px) {
|
|
22
|
+
.app-wrapper {
|
|
23
|
+
margin: 10px 0;
|
|
24
|
+
padding: 14px;
|
|
25
|
+
border-radius: 8px;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
</style>
|
package/src/global.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
declare module 'shared-ritm'
|
|
1
|
+
declare module 'shared-ritm'
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
export default {
|
|
3
|
-
name: 'ArrowDownIcon',
|
|
4
|
-
}
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<template>
|
|
8
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
9
|
-
<path
|
|
10
|
-
d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2Z"
|
|
11
|
-
stroke="#3F8CFF"
|
|
12
|
-
stroke-width="1.5"
|
|
13
|
-
stroke-miterlimit="10"
|
|
14
|
-
stroke-linecap="round"
|
|
15
|
-
stroke-linejoin="round"
|
|
16
|
-
/>
|
|
17
|
-
<path
|
|
18
|
-
d="M15.53 10.74L12 14.26L8.47003 10.74"
|
|
19
|
-
stroke="#3F8CFF"
|
|
20
|
-
stroke-width="1.5"
|
|
21
|
-
stroke-linecap="round"
|
|
22
|
-
stroke-linejoin="round"
|
|
23
|
-
/>
|
|
24
|
-
</svg>
|
|
25
|
-
</template>
|
|
1
|
+
<script>
|
|
2
|
+
export default {
|
|
3
|
+
name: 'ArrowDownIcon',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
9
|
+
<path
|
|
10
|
+
d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2Z"
|
|
11
|
+
stroke="#3F8CFF"
|
|
12
|
+
stroke-width="1.5"
|
|
13
|
+
stroke-miterlimit="10"
|
|
14
|
+
stroke-linecap="round"
|
|
15
|
+
stroke-linejoin="round"
|
|
16
|
+
/>
|
|
17
|
+
<path
|
|
18
|
+
d="M15.53 10.74L12 14.26L8.47003 10.74"
|
|
19
|
+
stroke="#3F8CFF"
|
|
20
|
+
stroke-width="1.5"
|
|
21
|
+
stroke-linecap="round"
|
|
22
|
+
stroke-linejoin="round"
|
|
23
|
+
/>
|
|
24
|
+
</svg>
|
|
25
|
+
</template>
|