changes
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
18
.nuxt/app.config.mjs
Normal file
18
.nuxt/app.config.mjs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
import { _replaceAppConfig } from '#app/config'
|
||||||
|
import { defuFn } from 'defu'
|
||||||
|
|
||||||
|
const inlineConfig = {
|
||||||
|
"nuxt": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vite - webpack is handled directly in #app/config
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.accept((newModule) => {
|
||||||
|
_replaceAppConfig(newModule.default)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default /*@__PURE__*/ defuFn(inlineConfig)
|
||||||
1
.nuxt/component-chunk.mjs
Normal file
1
.nuxt/component-chunk.mjs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export default {}
|
||||||
546
.nuxt/components.d.ts
vendored
Normal file
546
.nuxt/components.d.ts
vendored
Normal file
@@ -0,0 +1,546 @@
|
|||||||
|
|
||||||
|
import type { DefineComponent, SlotsType } from 'vue'
|
||||||
|
type IslandComponent<T extends DefineComponent> = T & DefineComponent<{}, {refresh: () => Promise<void>}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>>
|
||||||
|
type HydrationStrategies = {
|
||||||
|
hydrateOnVisible?: IntersectionObserverInit | true
|
||||||
|
hydrateOnIdle?: number | true
|
||||||
|
hydrateOnInteraction?: keyof HTMLElementEventMap | Array<keyof HTMLElementEventMap> | true
|
||||||
|
hydrateOnMediaQuery?: string
|
||||||
|
hydrateAfter?: number
|
||||||
|
hydrateWhen?: boolean
|
||||||
|
hydrateNever?: true
|
||||||
|
}
|
||||||
|
type LazyComponent<T> = (T & DefineComponent<HydrationStrategies, {}, {}, {}, {}, {}, {}, { hydrated: () => void }>)
|
||||||
|
interface _GlobalComponents {
|
||||||
|
'Caption': typeof import("../components/Caption.vue")['default']
|
||||||
|
'Modal': typeof import("../components/Modal.vue")['default']
|
||||||
|
'SearchBox': typeof import("../components/SearchBox.vue")['default']
|
||||||
|
'SvgIcon': typeof import("../components/SvgIcon.vue")['default']
|
||||||
|
'Tooltip': typeof import("../components/Tooltip.vue")['default']
|
||||||
|
'TopMenu': typeof import("../components/TopMenu.vue")['default']
|
||||||
|
'CommonAvatarbox': typeof import("../components/common/Avatarbox.vue")['default']
|
||||||
|
'CommonEditor': typeof import("../components/common/Editor.vue")['default']
|
||||||
|
'CommonInputEmail': typeof import("../components/common/InputEmail.vue")['default']
|
||||||
|
'CommonInputNumber': typeof import("../components/common/InputNumber.vue")['default']
|
||||||
|
'CommonInputPhone': typeof import("../components/common/InputPhone.vue")['default']
|
||||||
|
'CommonJobView': typeof import("../components/common/JobView.vue")['default']
|
||||||
|
'CommonNoteInfo': typeof import("../components/common/NoteInfo.vue")['default']
|
||||||
|
'CommonNotebox': typeof import("../components/common/Notebox.vue")['default']
|
||||||
|
'CommonPhone': typeof import("../components/common/Phone.vue")['default']
|
||||||
|
'DatatableContextMenu': typeof import("../components/datatable/ContextMenu.vue")['default']
|
||||||
|
'DatatableCreateTemplate': typeof import("../components/datatable/CreateTemplate.vue")['default']
|
||||||
|
'DatatableDataModel': typeof import("../components/datatable/DataModel.vue")['default']
|
||||||
|
'DatatableDataTable': typeof import("../components/datatable/DataTable.vue")['default']
|
||||||
|
'DatatableDataView': typeof import("../components/datatable/DataView.vue")['default']
|
||||||
|
'DatatableEditLabel': typeof import("../components/datatable/EditLabel.vue")['default']
|
||||||
|
'DatatableFieldAttribute': typeof import("../components/datatable/FieldAttribute.vue")['default']
|
||||||
|
'DatatableFilterOption': typeof import("../components/datatable/FilterOption.vue")['default']
|
||||||
|
'DatatableFormatOption': typeof import("../components/datatable/FormatOption.vue")['default']
|
||||||
|
'DatatableMenuSave': typeof import("../components/datatable/MenuSave.vue")['default']
|
||||||
|
'DatatableModelInfo': typeof import("../components/datatable/ModelInfo.vue")['default']
|
||||||
|
'DatatableNewField': typeof import("../components/datatable/NewField.vue")['default']
|
||||||
|
'DatatablePagination': typeof import("../components/datatable/Pagination.vue")['default']
|
||||||
|
'DatatableScrollBox': typeof import("../components/datatable/ScrollBox.vue")['default']
|
||||||
|
'DatatableTableOption': typeof import("../components/datatable/TableOption.vue")['default']
|
||||||
|
'DatatableTableSetting': typeof import("../components/datatable/TableSetting.vue")['default']
|
||||||
|
'DatatableTimeOption': typeof import("../components/datatable/TimeOption.vue")['default']
|
||||||
|
'DatatableFormatColorText': typeof import("../components/datatable/format/ColorText.vue")['default']
|
||||||
|
'DatatableFormatDate': typeof import("../components/datatable/format/FormatDate.vue")['default']
|
||||||
|
'DatatableFormatNumber': typeof import("../components/datatable/format/FormatNumber.vue")['default']
|
||||||
|
'DatatableFormatTime': typeof import("../components/datatable/format/FormatTime.vue")['default']
|
||||||
|
'Datepicker': typeof import("../components/datepicker/Datepicker.vue")['default']
|
||||||
|
'DatepickerEventDetail': typeof import("../components/datepicker/EventDetail.vue")['default']
|
||||||
|
'DatepickerEventMultiMonth': typeof import("../components/datepicker/EventMultiMonth.vue")['default']
|
||||||
|
'DatepickerEventOneMonth': typeof import("../components/datepicker/EventOneMonth.vue")['default']
|
||||||
|
'DatepickerEventSummary': typeof import("../components/datepicker/EventSummary.vue")['default']
|
||||||
|
'DatepickerPickDay': typeof import("../components/datepicker/PickDay.vue")['default']
|
||||||
|
'DatepickerPickMonth': typeof import("../components/datepicker/PickMonth.vue")['default']
|
||||||
|
'DatepickerPickYear': typeof import("../components/datepicker/PickYear.vue")['default']
|
||||||
|
'DatepickerViewEvent': typeof import("../components/datepicker/ViewEvent.vue")['default']
|
||||||
|
'DialogApprovalCode': typeof import("../components/dialog/ApprovalCode.vue")['default']
|
||||||
|
'DialogConfirm': typeof import("../components/dialog/Confirm.vue")['default']
|
||||||
|
'DialogCountDown': typeof import("../components/dialog/CountDown.vue")['default']
|
||||||
|
'DialogDelete': typeof import("../components/dialog/Delete.vue")['default']
|
||||||
|
'DialogError': typeof import("../components/dialog/Error.vue")['default']
|
||||||
|
'DialogInfo': typeof import("../components/dialog/Info.vue")['default']
|
||||||
|
'DialogSuccess': typeof import("../components/dialog/Success.vue")['default']
|
||||||
|
'MaintabConfiguration': typeof import("../components/maintab/Configuration.vue")['default']
|
||||||
|
'MaintabDataDeletion': typeof import("../components/maintab/DataDeletion.vue")['default']
|
||||||
|
'MediaCamera': typeof import("../components/media/Camera.vue")['default']
|
||||||
|
'MediaChipImage': typeof import("../components/media/ChipImage.vue")['default']
|
||||||
|
'MediaCropImage': typeof import("../components/media/CropImage.vue")['default']
|
||||||
|
'MediaFileCount': typeof import("../components/media/FileCount.vue")['default']
|
||||||
|
'MediaFileGallery': typeof import("../components/media/FileGallery.vue")['default']
|
||||||
|
'MediaFileInfo': typeof import("../components/media/FileInfo.vue")['default']
|
||||||
|
'MediaFileList': typeof import("../components/media/FileList.vue")['default']
|
||||||
|
'MediaFileShow': typeof import("../components/media/FileShow.vue")['default']
|
||||||
|
'MediaFileUpload': typeof import("../components/media/FileUpload.vue")['default']
|
||||||
|
'MediaImageAttr': typeof import("../components/media/ImageAttr.vue")['default']
|
||||||
|
'MediaImageGallery': typeof import("../components/media/ImageGallery.vue")['default']
|
||||||
|
'MediaImageShow': typeof import("../components/media/ImageShow.vue")['default']
|
||||||
|
'MediaImagebox': typeof import("../components/media/Imagebox.vue")['default']
|
||||||
|
'MediaPicture': typeof import("../components/media/Picture.vue")['default']
|
||||||
|
'MediaUploadProgress': typeof import("../components/media/UploadProgress.vue")['default']
|
||||||
|
'MediaYoutubeVideo': typeof import("../components/media/YoutubeVideo.vue")['default']
|
||||||
|
'MenuMarkData': typeof import("../components/menu/MarkData.vue")['default']
|
||||||
|
'MenuAction': typeof import("../components/menu/MenuAction.vue")['default']
|
||||||
|
'MenuAdd': typeof import("../components/menu/MenuAdd.vue")['default']
|
||||||
|
'MenuCV': typeof import("../components/menu/MenuCV.vue")['default']
|
||||||
|
'MenuCheck': typeof import("../components/menu/MenuCheck.vue")['default']
|
||||||
|
'MenuCollab': typeof import("../components/menu/MenuCollab.vue")['default']
|
||||||
|
'MenuDocker': typeof import("../components/menu/MenuDocker.vue")['default']
|
||||||
|
'MenuParam': typeof import("../components/menu/MenuParam.vue")['default']
|
||||||
|
'MenuPhone': typeof import("../components/menu/MenuPhone.vue")['default']
|
||||||
|
'MenuRights': typeof import("../components/menu/MenuRights.vue")['default']
|
||||||
|
'MenuStaff': typeof import("../components/menu/MenuStaff.vue")['default']
|
||||||
|
'MenuUser': typeof import("../components/menu/MenuUser.vue")['default']
|
||||||
|
'ParameterApps': typeof import("../components/parameter/Apps.vue")['default']
|
||||||
|
'ParameterCodeName': typeof import("../components/parameter/CodeName.vue")['default']
|
||||||
|
'ParameterCommon': typeof import("../components/parameter/Common.vue")['default']
|
||||||
|
'ParameterDataSetting': typeof import("../components/parameter/DataSetting.vue")['default']
|
||||||
|
'ParameterEmailSetup': typeof import("../components/parameter/EmailSetup.vue")['default']
|
||||||
|
'ParameterNameDetail': typeof import("../components/parameter/NameDetail.vue")['default']
|
||||||
|
'ParameterSettingFields': typeof import("../components/parameter/SettingFields.vue")['default']
|
||||||
|
'ServerContainerMgnt': typeof import("../components/server/ContainerMgnt.vue")['default']
|
||||||
|
'ServerCpuRam': typeof import("../components/server/CpuRam.vue")['default']
|
||||||
|
'ServerDiskInfo': typeof import("../components/server/DiskInfo.vue")['default']
|
||||||
|
'ServerDockerContainer': typeof import("../components/server/DockerContainer.vue")['default']
|
||||||
|
'ServerInfo': typeof import("../components/server/ServerInfo.vue")['default']
|
||||||
|
'SnackbarError': typeof import("../components/snackbar/Error.vue")['default']
|
||||||
|
'SnackbarInfo': typeof import("../components/snackbar/Info.vue")['default']
|
||||||
|
'SnackbarSnackBar': typeof import("../components/snackbar/SnackBar.vue")['default']
|
||||||
|
'SnackbarSuccess': typeof import("../components/snackbar/Success.vue")['default']
|
||||||
|
'UserChangePass': typeof import("../components/user/ChangePass.vue")['default']
|
||||||
|
'UserConfirmCode': typeof import("../components/user/ConfirmCode.vue")['default']
|
||||||
|
'UserCreateUser': typeof import("../components/user/CreateUser.vue")['default']
|
||||||
|
'UserEditUser': typeof import("../components/user/EditUser.vue")['default']
|
||||||
|
'UserLogout': typeof import("../components/user/Logout.vue")['default']
|
||||||
|
'UserNewUser': typeof import("../components/user/NewUser.vue")['default']
|
||||||
|
'UserProfile': typeof import("../components/user/Profile.vue")['default']
|
||||||
|
'UserSetPassword': typeof import("../components/user/SetPassword.vue")['default']
|
||||||
|
'UserInfo': typeof import("../components/user/UserInfo.vue")['default']
|
||||||
|
'NuxtWelcome': typeof import("../node_modules/nuxt/dist/app/components/welcome.vue")['default']
|
||||||
|
'NuxtLayout': typeof import("../node_modules/nuxt/dist/app/components/nuxt-layout")['default']
|
||||||
|
'NuxtErrorBoundary': typeof import("../node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue")['default']
|
||||||
|
'ClientOnly': typeof import("../node_modules/nuxt/dist/app/components/client-only")['default']
|
||||||
|
'DevOnly': typeof import("../node_modules/nuxt/dist/app/components/dev-only")['default']
|
||||||
|
'ServerPlaceholder': typeof import("../node_modules/nuxt/dist/app/components/server-placeholder")['default']
|
||||||
|
'NuxtLink': typeof import("../node_modules/nuxt/dist/app/components/nuxt-link")['default']
|
||||||
|
'NuxtLoadingIndicator': typeof import("../node_modules/nuxt/dist/app/components/nuxt-loading-indicator")['default']
|
||||||
|
'NuxtTime': typeof import("../node_modules/nuxt/dist/app/components/nuxt-time.vue")['default']
|
||||||
|
'NuxtRouteAnnouncer': typeof import("../node_modules/nuxt/dist/app/components/nuxt-route-announcer")['default']
|
||||||
|
'NuxtImg': typeof import("../node_modules/@nuxt/image/dist/runtime/components/NuxtImg.vue")['default']
|
||||||
|
'NuxtPicture': typeof import("../node_modules/@nuxt/image/dist/runtime/components/NuxtPicture.vue")['default']
|
||||||
|
'NuxtPage': typeof import("../node_modules/nuxt/dist/pages/runtime/page")['default']
|
||||||
|
'NoScript': typeof import("../node_modules/nuxt/dist/head/runtime/components")['NoScript']
|
||||||
|
'Link': typeof import("../node_modules/nuxt/dist/head/runtime/components")['Link']
|
||||||
|
'Base': typeof import("../node_modules/nuxt/dist/head/runtime/components")['Base']
|
||||||
|
'Title': typeof import("../node_modules/nuxt/dist/head/runtime/components")['Title']
|
||||||
|
'Meta': typeof import("../node_modules/nuxt/dist/head/runtime/components")['Meta']
|
||||||
|
'Style': typeof import("../node_modules/nuxt/dist/head/runtime/components")['Style']
|
||||||
|
'Head': typeof import("../node_modules/nuxt/dist/head/runtime/components")['Head']
|
||||||
|
'Html': typeof import("../node_modules/nuxt/dist/head/runtime/components")['Html']
|
||||||
|
'Body': typeof import("../node_modules/nuxt/dist/head/runtime/components")['Body']
|
||||||
|
'NuxtIsland': typeof import("../node_modules/nuxt/dist/app/components/nuxt-island")['default']
|
||||||
|
'NuxtRouteAnnouncer': IslandComponent<typeof import("../node_modules/nuxt/dist/app/components/server-placeholder")['default']>
|
||||||
|
'LazyCaption': LazyComponent<typeof import("../components/Caption.vue")['default']>
|
||||||
|
'LazyModal': LazyComponent<typeof import("../components/Modal.vue")['default']>
|
||||||
|
'LazySearchBox': LazyComponent<typeof import("../components/SearchBox.vue")['default']>
|
||||||
|
'LazySvgIcon': LazyComponent<typeof import("../components/SvgIcon.vue")['default']>
|
||||||
|
'LazyTooltip': LazyComponent<typeof import("../components/Tooltip.vue")['default']>
|
||||||
|
'LazyTopMenu': LazyComponent<typeof import("../components/TopMenu.vue")['default']>
|
||||||
|
'LazyCommonAvatarbox': LazyComponent<typeof import("../components/common/Avatarbox.vue")['default']>
|
||||||
|
'LazyCommonEditor': LazyComponent<typeof import("../components/common/Editor.vue")['default']>
|
||||||
|
'LazyCommonInputEmail': LazyComponent<typeof import("../components/common/InputEmail.vue")['default']>
|
||||||
|
'LazyCommonInputNumber': LazyComponent<typeof import("../components/common/InputNumber.vue")['default']>
|
||||||
|
'LazyCommonInputPhone': LazyComponent<typeof import("../components/common/InputPhone.vue")['default']>
|
||||||
|
'LazyCommonJobView': LazyComponent<typeof import("../components/common/JobView.vue")['default']>
|
||||||
|
'LazyCommonNoteInfo': LazyComponent<typeof import("../components/common/NoteInfo.vue")['default']>
|
||||||
|
'LazyCommonNotebox': LazyComponent<typeof import("../components/common/Notebox.vue")['default']>
|
||||||
|
'LazyCommonPhone': LazyComponent<typeof import("../components/common/Phone.vue")['default']>
|
||||||
|
'LazyDatatableContextMenu': LazyComponent<typeof import("../components/datatable/ContextMenu.vue")['default']>
|
||||||
|
'LazyDatatableCreateTemplate': LazyComponent<typeof import("../components/datatable/CreateTemplate.vue")['default']>
|
||||||
|
'LazyDatatableDataModel': LazyComponent<typeof import("../components/datatable/DataModel.vue")['default']>
|
||||||
|
'LazyDatatableDataTable': LazyComponent<typeof import("../components/datatable/DataTable.vue")['default']>
|
||||||
|
'LazyDatatableDataView': LazyComponent<typeof import("../components/datatable/DataView.vue")['default']>
|
||||||
|
'LazyDatatableEditLabel': LazyComponent<typeof import("../components/datatable/EditLabel.vue")['default']>
|
||||||
|
'LazyDatatableFieldAttribute': LazyComponent<typeof import("../components/datatable/FieldAttribute.vue")['default']>
|
||||||
|
'LazyDatatableFilterOption': LazyComponent<typeof import("../components/datatable/FilterOption.vue")['default']>
|
||||||
|
'LazyDatatableFormatOption': LazyComponent<typeof import("../components/datatable/FormatOption.vue")['default']>
|
||||||
|
'LazyDatatableMenuSave': LazyComponent<typeof import("../components/datatable/MenuSave.vue")['default']>
|
||||||
|
'LazyDatatableModelInfo': LazyComponent<typeof import("../components/datatable/ModelInfo.vue")['default']>
|
||||||
|
'LazyDatatableNewField': LazyComponent<typeof import("../components/datatable/NewField.vue")['default']>
|
||||||
|
'LazyDatatablePagination': LazyComponent<typeof import("../components/datatable/Pagination.vue")['default']>
|
||||||
|
'LazyDatatableScrollBox': LazyComponent<typeof import("../components/datatable/ScrollBox.vue")['default']>
|
||||||
|
'LazyDatatableTableOption': LazyComponent<typeof import("../components/datatable/TableOption.vue")['default']>
|
||||||
|
'LazyDatatableTableSetting': LazyComponent<typeof import("../components/datatable/TableSetting.vue")['default']>
|
||||||
|
'LazyDatatableTimeOption': LazyComponent<typeof import("../components/datatable/TimeOption.vue")['default']>
|
||||||
|
'LazyDatatableFormatColorText': LazyComponent<typeof import("../components/datatable/format/ColorText.vue")['default']>
|
||||||
|
'LazyDatatableFormatDate': LazyComponent<typeof import("../components/datatable/format/FormatDate.vue")['default']>
|
||||||
|
'LazyDatatableFormatNumber': LazyComponent<typeof import("../components/datatable/format/FormatNumber.vue")['default']>
|
||||||
|
'LazyDatatableFormatTime': LazyComponent<typeof import("../components/datatable/format/FormatTime.vue")['default']>
|
||||||
|
'LazyDatepicker': LazyComponent<typeof import("../components/datepicker/Datepicker.vue")['default']>
|
||||||
|
'LazyDatepickerEventDetail': LazyComponent<typeof import("../components/datepicker/EventDetail.vue")['default']>
|
||||||
|
'LazyDatepickerEventMultiMonth': LazyComponent<typeof import("../components/datepicker/EventMultiMonth.vue")['default']>
|
||||||
|
'LazyDatepickerEventOneMonth': LazyComponent<typeof import("../components/datepicker/EventOneMonth.vue")['default']>
|
||||||
|
'LazyDatepickerEventSummary': LazyComponent<typeof import("../components/datepicker/EventSummary.vue")['default']>
|
||||||
|
'LazyDatepickerPickDay': LazyComponent<typeof import("../components/datepicker/PickDay.vue")['default']>
|
||||||
|
'LazyDatepickerPickMonth': LazyComponent<typeof import("../components/datepicker/PickMonth.vue")['default']>
|
||||||
|
'LazyDatepickerPickYear': LazyComponent<typeof import("../components/datepicker/PickYear.vue")['default']>
|
||||||
|
'LazyDatepickerViewEvent': LazyComponent<typeof import("../components/datepicker/ViewEvent.vue")['default']>
|
||||||
|
'LazyDialogApprovalCode': LazyComponent<typeof import("../components/dialog/ApprovalCode.vue")['default']>
|
||||||
|
'LazyDialogConfirm': LazyComponent<typeof import("../components/dialog/Confirm.vue")['default']>
|
||||||
|
'LazyDialogCountDown': LazyComponent<typeof import("../components/dialog/CountDown.vue")['default']>
|
||||||
|
'LazyDialogDelete': LazyComponent<typeof import("../components/dialog/Delete.vue")['default']>
|
||||||
|
'LazyDialogError': LazyComponent<typeof import("../components/dialog/Error.vue")['default']>
|
||||||
|
'LazyDialogInfo': LazyComponent<typeof import("../components/dialog/Info.vue")['default']>
|
||||||
|
'LazyDialogSuccess': LazyComponent<typeof import("../components/dialog/Success.vue")['default']>
|
||||||
|
'LazyMaintabConfiguration': LazyComponent<typeof import("../components/maintab/Configuration.vue")['default']>
|
||||||
|
'LazyMaintabDataDeletion': LazyComponent<typeof import("../components/maintab/DataDeletion.vue")['default']>
|
||||||
|
'LazyMediaCamera': LazyComponent<typeof import("../components/media/Camera.vue")['default']>
|
||||||
|
'LazyMediaChipImage': LazyComponent<typeof import("../components/media/ChipImage.vue")['default']>
|
||||||
|
'LazyMediaCropImage': LazyComponent<typeof import("../components/media/CropImage.vue")['default']>
|
||||||
|
'LazyMediaFileCount': LazyComponent<typeof import("../components/media/FileCount.vue")['default']>
|
||||||
|
'LazyMediaFileGallery': LazyComponent<typeof import("../components/media/FileGallery.vue")['default']>
|
||||||
|
'LazyMediaFileInfo': LazyComponent<typeof import("../components/media/FileInfo.vue")['default']>
|
||||||
|
'LazyMediaFileList': LazyComponent<typeof import("../components/media/FileList.vue")['default']>
|
||||||
|
'LazyMediaFileShow': LazyComponent<typeof import("../components/media/FileShow.vue")['default']>
|
||||||
|
'LazyMediaFileUpload': LazyComponent<typeof import("../components/media/FileUpload.vue")['default']>
|
||||||
|
'LazyMediaImageAttr': LazyComponent<typeof import("../components/media/ImageAttr.vue")['default']>
|
||||||
|
'LazyMediaImageGallery': LazyComponent<typeof import("../components/media/ImageGallery.vue")['default']>
|
||||||
|
'LazyMediaImageShow': LazyComponent<typeof import("../components/media/ImageShow.vue")['default']>
|
||||||
|
'LazyMediaImagebox': LazyComponent<typeof import("../components/media/Imagebox.vue")['default']>
|
||||||
|
'LazyMediaPicture': LazyComponent<typeof import("../components/media/Picture.vue")['default']>
|
||||||
|
'LazyMediaUploadProgress': LazyComponent<typeof import("../components/media/UploadProgress.vue")['default']>
|
||||||
|
'LazyMediaYoutubeVideo': LazyComponent<typeof import("../components/media/YoutubeVideo.vue")['default']>
|
||||||
|
'LazyMenuMarkData': LazyComponent<typeof import("../components/menu/MarkData.vue")['default']>
|
||||||
|
'LazyMenuAction': LazyComponent<typeof import("../components/menu/MenuAction.vue")['default']>
|
||||||
|
'LazyMenuAdd': LazyComponent<typeof import("../components/menu/MenuAdd.vue")['default']>
|
||||||
|
'LazyMenuCV': LazyComponent<typeof import("../components/menu/MenuCV.vue")['default']>
|
||||||
|
'LazyMenuCheck': LazyComponent<typeof import("../components/menu/MenuCheck.vue")['default']>
|
||||||
|
'LazyMenuCollab': LazyComponent<typeof import("../components/menu/MenuCollab.vue")['default']>
|
||||||
|
'LazyMenuDocker': LazyComponent<typeof import("../components/menu/MenuDocker.vue")['default']>
|
||||||
|
'LazyMenuParam': LazyComponent<typeof import("../components/menu/MenuParam.vue")['default']>
|
||||||
|
'LazyMenuPhone': LazyComponent<typeof import("../components/menu/MenuPhone.vue")['default']>
|
||||||
|
'LazyMenuRights': LazyComponent<typeof import("../components/menu/MenuRights.vue")['default']>
|
||||||
|
'LazyMenuStaff': LazyComponent<typeof import("../components/menu/MenuStaff.vue")['default']>
|
||||||
|
'LazyMenuUser': LazyComponent<typeof import("../components/menu/MenuUser.vue")['default']>
|
||||||
|
'LazyParameterApps': LazyComponent<typeof import("../components/parameter/Apps.vue")['default']>
|
||||||
|
'LazyParameterCodeName': LazyComponent<typeof import("../components/parameter/CodeName.vue")['default']>
|
||||||
|
'LazyParameterCommon': LazyComponent<typeof import("../components/parameter/Common.vue")['default']>
|
||||||
|
'LazyParameterDataSetting': LazyComponent<typeof import("../components/parameter/DataSetting.vue")['default']>
|
||||||
|
'LazyParameterEmailSetup': LazyComponent<typeof import("../components/parameter/EmailSetup.vue")['default']>
|
||||||
|
'LazyParameterNameDetail': LazyComponent<typeof import("../components/parameter/NameDetail.vue")['default']>
|
||||||
|
'LazyParameterSettingFields': LazyComponent<typeof import("../components/parameter/SettingFields.vue")['default']>
|
||||||
|
'LazyServerContainerMgnt': LazyComponent<typeof import("../components/server/ContainerMgnt.vue")['default']>
|
||||||
|
'LazyServerCpuRam': LazyComponent<typeof import("../components/server/CpuRam.vue")['default']>
|
||||||
|
'LazyServerDiskInfo': LazyComponent<typeof import("../components/server/DiskInfo.vue")['default']>
|
||||||
|
'LazyServerDockerContainer': LazyComponent<typeof import("../components/server/DockerContainer.vue")['default']>
|
||||||
|
'LazyServerInfo': LazyComponent<typeof import("../components/server/ServerInfo.vue")['default']>
|
||||||
|
'LazySnackbarError': LazyComponent<typeof import("../components/snackbar/Error.vue")['default']>
|
||||||
|
'LazySnackbarInfo': LazyComponent<typeof import("../components/snackbar/Info.vue")['default']>
|
||||||
|
'LazySnackbarSnackBar': LazyComponent<typeof import("../components/snackbar/SnackBar.vue")['default']>
|
||||||
|
'LazySnackbarSuccess': LazyComponent<typeof import("../components/snackbar/Success.vue")['default']>
|
||||||
|
'LazyUserChangePass': LazyComponent<typeof import("../components/user/ChangePass.vue")['default']>
|
||||||
|
'LazyUserConfirmCode': LazyComponent<typeof import("../components/user/ConfirmCode.vue")['default']>
|
||||||
|
'LazyUserCreateUser': LazyComponent<typeof import("../components/user/CreateUser.vue")['default']>
|
||||||
|
'LazyUserEditUser': LazyComponent<typeof import("../components/user/EditUser.vue")['default']>
|
||||||
|
'LazyUserLogout': LazyComponent<typeof import("../components/user/Logout.vue")['default']>
|
||||||
|
'LazyUserNewUser': LazyComponent<typeof import("../components/user/NewUser.vue")['default']>
|
||||||
|
'LazyUserProfile': LazyComponent<typeof import("../components/user/Profile.vue")['default']>
|
||||||
|
'LazyUserSetPassword': LazyComponent<typeof import("../components/user/SetPassword.vue")['default']>
|
||||||
|
'LazyUserInfo': LazyComponent<typeof import("../components/user/UserInfo.vue")['default']>
|
||||||
|
'LazyNuxtWelcome': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/welcome.vue")['default']>
|
||||||
|
'LazyNuxtLayout': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-layout")['default']>
|
||||||
|
'LazyNuxtErrorBoundary': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue")['default']>
|
||||||
|
'LazyClientOnly': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/client-only")['default']>
|
||||||
|
'LazyDevOnly': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/dev-only")['default']>
|
||||||
|
'LazyServerPlaceholder': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/server-placeholder")['default']>
|
||||||
|
'LazyNuxtLink': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-link")['default']>
|
||||||
|
'LazyNuxtLoadingIndicator': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-loading-indicator")['default']>
|
||||||
|
'LazyNuxtTime': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-time.vue")['default']>
|
||||||
|
'LazyNuxtRouteAnnouncer': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-route-announcer")['default']>
|
||||||
|
'LazyNuxtImg': LazyComponent<typeof import("../node_modules/@nuxt/image/dist/runtime/components/NuxtImg.vue")['default']>
|
||||||
|
'LazyNuxtPicture': LazyComponent<typeof import("../node_modules/@nuxt/image/dist/runtime/components/NuxtPicture.vue")['default']>
|
||||||
|
'LazyNuxtPage': LazyComponent<typeof import("../node_modules/nuxt/dist/pages/runtime/page")['default']>
|
||||||
|
'LazyNoScript': LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['NoScript']>
|
||||||
|
'LazyLink': LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Link']>
|
||||||
|
'LazyBase': LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Base']>
|
||||||
|
'LazyTitle': LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Title']>
|
||||||
|
'LazyMeta': LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Meta']>
|
||||||
|
'LazyStyle': LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Style']>
|
||||||
|
'LazyHead': LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Head']>
|
||||||
|
'LazyHtml': LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Html']>
|
||||||
|
'LazyBody': LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Body']>
|
||||||
|
'LazyNuxtIsland': LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-island")['default']>
|
||||||
|
'LazyNuxtRouteAnnouncer': LazyComponent<IslandComponent<typeof import("../node_modules/nuxt/dist/app/components/server-placeholder")['default']>>
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents extends _GlobalComponents { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Caption: typeof import("../components/Caption.vue")['default']
|
||||||
|
export const Modal: typeof import("../components/Modal.vue")['default']
|
||||||
|
export const SearchBox: typeof import("../components/SearchBox.vue")['default']
|
||||||
|
export const SvgIcon: typeof import("../components/SvgIcon.vue")['default']
|
||||||
|
export const Tooltip: typeof import("../components/Tooltip.vue")['default']
|
||||||
|
export const TopMenu: typeof import("../components/TopMenu.vue")['default']
|
||||||
|
export const CommonAvatarbox: typeof import("../components/common/Avatarbox.vue")['default']
|
||||||
|
export const CommonEditor: typeof import("../components/common/Editor.vue")['default']
|
||||||
|
export const CommonInputEmail: typeof import("../components/common/InputEmail.vue")['default']
|
||||||
|
export const CommonInputNumber: typeof import("../components/common/InputNumber.vue")['default']
|
||||||
|
export const CommonInputPhone: typeof import("../components/common/InputPhone.vue")['default']
|
||||||
|
export const CommonJobView: typeof import("../components/common/JobView.vue")['default']
|
||||||
|
export const CommonNoteInfo: typeof import("../components/common/NoteInfo.vue")['default']
|
||||||
|
export const CommonNotebox: typeof import("../components/common/Notebox.vue")['default']
|
||||||
|
export const CommonPhone: typeof import("../components/common/Phone.vue")['default']
|
||||||
|
export const DatatableContextMenu: typeof import("../components/datatable/ContextMenu.vue")['default']
|
||||||
|
export const DatatableCreateTemplate: typeof import("../components/datatable/CreateTemplate.vue")['default']
|
||||||
|
export const DatatableDataModel: typeof import("../components/datatable/DataModel.vue")['default']
|
||||||
|
export const DatatableDataTable: typeof import("../components/datatable/DataTable.vue")['default']
|
||||||
|
export const DatatableDataView: typeof import("../components/datatable/DataView.vue")['default']
|
||||||
|
export const DatatableEditLabel: typeof import("../components/datatable/EditLabel.vue")['default']
|
||||||
|
export const DatatableFieldAttribute: typeof import("../components/datatable/FieldAttribute.vue")['default']
|
||||||
|
export const DatatableFilterOption: typeof import("../components/datatable/FilterOption.vue")['default']
|
||||||
|
export const DatatableFormatOption: typeof import("../components/datatable/FormatOption.vue")['default']
|
||||||
|
export const DatatableMenuSave: typeof import("../components/datatable/MenuSave.vue")['default']
|
||||||
|
export const DatatableModelInfo: typeof import("../components/datatable/ModelInfo.vue")['default']
|
||||||
|
export const DatatableNewField: typeof import("../components/datatable/NewField.vue")['default']
|
||||||
|
export const DatatablePagination: typeof import("../components/datatable/Pagination.vue")['default']
|
||||||
|
export const DatatableScrollBox: typeof import("../components/datatable/ScrollBox.vue")['default']
|
||||||
|
export const DatatableTableOption: typeof import("../components/datatable/TableOption.vue")['default']
|
||||||
|
export const DatatableTableSetting: typeof import("../components/datatable/TableSetting.vue")['default']
|
||||||
|
export const DatatableTimeOption: typeof import("../components/datatable/TimeOption.vue")['default']
|
||||||
|
export const DatatableFormatColorText: typeof import("../components/datatable/format/ColorText.vue")['default']
|
||||||
|
export const DatatableFormatDate: typeof import("../components/datatable/format/FormatDate.vue")['default']
|
||||||
|
export const DatatableFormatNumber: typeof import("../components/datatable/format/FormatNumber.vue")['default']
|
||||||
|
export const DatatableFormatTime: typeof import("../components/datatable/format/FormatTime.vue")['default']
|
||||||
|
export const Datepicker: typeof import("../components/datepicker/Datepicker.vue")['default']
|
||||||
|
export const DatepickerEventDetail: typeof import("../components/datepicker/EventDetail.vue")['default']
|
||||||
|
export const DatepickerEventMultiMonth: typeof import("../components/datepicker/EventMultiMonth.vue")['default']
|
||||||
|
export const DatepickerEventOneMonth: typeof import("../components/datepicker/EventOneMonth.vue")['default']
|
||||||
|
export const DatepickerEventSummary: typeof import("../components/datepicker/EventSummary.vue")['default']
|
||||||
|
export const DatepickerPickDay: typeof import("../components/datepicker/PickDay.vue")['default']
|
||||||
|
export const DatepickerPickMonth: typeof import("../components/datepicker/PickMonth.vue")['default']
|
||||||
|
export const DatepickerPickYear: typeof import("../components/datepicker/PickYear.vue")['default']
|
||||||
|
export const DatepickerViewEvent: typeof import("../components/datepicker/ViewEvent.vue")['default']
|
||||||
|
export const DialogApprovalCode: typeof import("../components/dialog/ApprovalCode.vue")['default']
|
||||||
|
export const DialogConfirm: typeof import("../components/dialog/Confirm.vue")['default']
|
||||||
|
export const DialogCountDown: typeof import("../components/dialog/CountDown.vue")['default']
|
||||||
|
export const DialogDelete: typeof import("../components/dialog/Delete.vue")['default']
|
||||||
|
export const DialogError: typeof import("../components/dialog/Error.vue")['default']
|
||||||
|
export const DialogInfo: typeof import("../components/dialog/Info.vue")['default']
|
||||||
|
export const DialogSuccess: typeof import("../components/dialog/Success.vue")['default']
|
||||||
|
export const MaintabConfiguration: typeof import("../components/maintab/Configuration.vue")['default']
|
||||||
|
export const MaintabDataDeletion: typeof import("../components/maintab/DataDeletion.vue")['default']
|
||||||
|
export const MediaCamera: typeof import("../components/media/Camera.vue")['default']
|
||||||
|
export const MediaChipImage: typeof import("../components/media/ChipImage.vue")['default']
|
||||||
|
export const MediaCropImage: typeof import("../components/media/CropImage.vue")['default']
|
||||||
|
export const MediaFileCount: typeof import("../components/media/FileCount.vue")['default']
|
||||||
|
export const MediaFileGallery: typeof import("../components/media/FileGallery.vue")['default']
|
||||||
|
export const MediaFileInfo: typeof import("../components/media/FileInfo.vue")['default']
|
||||||
|
export const MediaFileList: typeof import("../components/media/FileList.vue")['default']
|
||||||
|
export const MediaFileShow: typeof import("../components/media/FileShow.vue")['default']
|
||||||
|
export const MediaFileUpload: typeof import("../components/media/FileUpload.vue")['default']
|
||||||
|
export const MediaImageAttr: typeof import("../components/media/ImageAttr.vue")['default']
|
||||||
|
export const MediaImageGallery: typeof import("../components/media/ImageGallery.vue")['default']
|
||||||
|
export const MediaImageShow: typeof import("../components/media/ImageShow.vue")['default']
|
||||||
|
export const MediaImagebox: typeof import("../components/media/Imagebox.vue")['default']
|
||||||
|
export const MediaPicture: typeof import("../components/media/Picture.vue")['default']
|
||||||
|
export const MediaUploadProgress: typeof import("../components/media/UploadProgress.vue")['default']
|
||||||
|
export const MediaYoutubeVideo: typeof import("../components/media/YoutubeVideo.vue")['default']
|
||||||
|
export const MenuMarkData: typeof import("../components/menu/MarkData.vue")['default']
|
||||||
|
export const MenuAction: typeof import("../components/menu/MenuAction.vue")['default']
|
||||||
|
export const MenuAdd: typeof import("../components/menu/MenuAdd.vue")['default']
|
||||||
|
export const MenuCV: typeof import("../components/menu/MenuCV.vue")['default']
|
||||||
|
export const MenuCheck: typeof import("../components/menu/MenuCheck.vue")['default']
|
||||||
|
export const MenuCollab: typeof import("../components/menu/MenuCollab.vue")['default']
|
||||||
|
export const MenuDocker: typeof import("../components/menu/MenuDocker.vue")['default']
|
||||||
|
export const MenuParam: typeof import("../components/menu/MenuParam.vue")['default']
|
||||||
|
export const MenuPhone: typeof import("../components/menu/MenuPhone.vue")['default']
|
||||||
|
export const MenuRights: typeof import("../components/menu/MenuRights.vue")['default']
|
||||||
|
export const MenuStaff: typeof import("../components/menu/MenuStaff.vue")['default']
|
||||||
|
export const MenuUser: typeof import("../components/menu/MenuUser.vue")['default']
|
||||||
|
export const ParameterApps: typeof import("../components/parameter/Apps.vue")['default']
|
||||||
|
export const ParameterCodeName: typeof import("../components/parameter/CodeName.vue")['default']
|
||||||
|
export const ParameterCommon: typeof import("../components/parameter/Common.vue")['default']
|
||||||
|
export const ParameterDataSetting: typeof import("../components/parameter/DataSetting.vue")['default']
|
||||||
|
export const ParameterEmailSetup: typeof import("../components/parameter/EmailSetup.vue")['default']
|
||||||
|
export const ParameterNameDetail: typeof import("../components/parameter/NameDetail.vue")['default']
|
||||||
|
export const ParameterSettingFields: typeof import("../components/parameter/SettingFields.vue")['default']
|
||||||
|
export const ServerContainerMgnt: typeof import("../components/server/ContainerMgnt.vue")['default']
|
||||||
|
export const ServerCpuRam: typeof import("../components/server/CpuRam.vue")['default']
|
||||||
|
export const ServerDiskInfo: typeof import("../components/server/DiskInfo.vue")['default']
|
||||||
|
export const ServerDockerContainer: typeof import("../components/server/DockerContainer.vue")['default']
|
||||||
|
export const ServerInfo: typeof import("../components/server/ServerInfo.vue")['default']
|
||||||
|
export const SnackbarError: typeof import("../components/snackbar/Error.vue")['default']
|
||||||
|
export const SnackbarInfo: typeof import("../components/snackbar/Info.vue")['default']
|
||||||
|
export const SnackbarSnackBar: typeof import("../components/snackbar/SnackBar.vue")['default']
|
||||||
|
export const SnackbarSuccess: typeof import("../components/snackbar/Success.vue")['default']
|
||||||
|
export const UserChangePass: typeof import("../components/user/ChangePass.vue")['default']
|
||||||
|
export const UserConfirmCode: typeof import("../components/user/ConfirmCode.vue")['default']
|
||||||
|
export const UserCreateUser: typeof import("../components/user/CreateUser.vue")['default']
|
||||||
|
export const UserEditUser: typeof import("../components/user/EditUser.vue")['default']
|
||||||
|
export const UserLogout: typeof import("../components/user/Logout.vue")['default']
|
||||||
|
export const UserNewUser: typeof import("../components/user/NewUser.vue")['default']
|
||||||
|
export const UserProfile: typeof import("../components/user/Profile.vue")['default']
|
||||||
|
export const UserSetPassword: typeof import("../components/user/SetPassword.vue")['default']
|
||||||
|
export const UserInfo: typeof import("../components/user/UserInfo.vue")['default']
|
||||||
|
export const NuxtWelcome: typeof import("../node_modules/nuxt/dist/app/components/welcome.vue")['default']
|
||||||
|
export const NuxtLayout: typeof import("../node_modules/nuxt/dist/app/components/nuxt-layout")['default']
|
||||||
|
export const NuxtErrorBoundary: typeof import("../node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue")['default']
|
||||||
|
export const ClientOnly: typeof import("../node_modules/nuxt/dist/app/components/client-only")['default']
|
||||||
|
export const DevOnly: typeof import("../node_modules/nuxt/dist/app/components/dev-only")['default']
|
||||||
|
export const ServerPlaceholder: typeof import("../node_modules/nuxt/dist/app/components/server-placeholder")['default']
|
||||||
|
export const NuxtLink: typeof import("../node_modules/nuxt/dist/app/components/nuxt-link")['default']
|
||||||
|
export const NuxtLoadingIndicator: typeof import("../node_modules/nuxt/dist/app/components/nuxt-loading-indicator")['default']
|
||||||
|
export const NuxtTime: typeof import("../node_modules/nuxt/dist/app/components/nuxt-time.vue")['default']
|
||||||
|
export const NuxtRouteAnnouncer: typeof import("../node_modules/nuxt/dist/app/components/nuxt-route-announcer")['default']
|
||||||
|
export const NuxtImg: typeof import("../node_modules/@nuxt/image/dist/runtime/components/NuxtImg.vue")['default']
|
||||||
|
export const NuxtPicture: typeof import("../node_modules/@nuxt/image/dist/runtime/components/NuxtPicture.vue")['default']
|
||||||
|
export const NuxtPage: typeof import("../node_modules/nuxt/dist/pages/runtime/page")['default']
|
||||||
|
export const NoScript: typeof import("../node_modules/nuxt/dist/head/runtime/components")['NoScript']
|
||||||
|
export const Link: typeof import("../node_modules/nuxt/dist/head/runtime/components")['Link']
|
||||||
|
export const Base: typeof import("../node_modules/nuxt/dist/head/runtime/components")['Base']
|
||||||
|
export const Title: typeof import("../node_modules/nuxt/dist/head/runtime/components")['Title']
|
||||||
|
export const Meta: typeof import("../node_modules/nuxt/dist/head/runtime/components")['Meta']
|
||||||
|
export const Style: typeof import("../node_modules/nuxt/dist/head/runtime/components")['Style']
|
||||||
|
export const Head: typeof import("../node_modules/nuxt/dist/head/runtime/components")['Head']
|
||||||
|
export const Html: typeof import("../node_modules/nuxt/dist/head/runtime/components")['Html']
|
||||||
|
export const Body: typeof import("../node_modules/nuxt/dist/head/runtime/components")['Body']
|
||||||
|
export const NuxtIsland: typeof import("../node_modules/nuxt/dist/app/components/nuxt-island")['default']
|
||||||
|
export const NuxtRouteAnnouncer: IslandComponent<typeof import("../node_modules/nuxt/dist/app/components/server-placeholder")['default']>
|
||||||
|
export const LazyCaption: LazyComponent<typeof import("../components/Caption.vue")['default']>
|
||||||
|
export const LazyModal: LazyComponent<typeof import("../components/Modal.vue")['default']>
|
||||||
|
export const LazySearchBox: LazyComponent<typeof import("../components/SearchBox.vue")['default']>
|
||||||
|
export const LazySvgIcon: LazyComponent<typeof import("../components/SvgIcon.vue")['default']>
|
||||||
|
export const LazyTooltip: LazyComponent<typeof import("../components/Tooltip.vue")['default']>
|
||||||
|
export const LazyTopMenu: LazyComponent<typeof import("../components/TopMenu.vue")['default']>
|
||||||
|
export const LazyCommonAvatarbox: LazyComponent<typeof import("../components/common/Avatarbox.vue")['default']>
|
||||||
|
export const LazyCommonEditor: LazyComponent<typeof import("../components/common/Editor.vue")['default']>
|
||||||
|
export const LazyCommonInputEmail: LazyComponent<typeof import("../components/common/InputEmail.vue")['default']>
|
||||||
|
export const LazyCommonInputNumber: LazyComponent<typeof import("../components/common/InputNumber.vue")['default']>
|
||||||
|
export const LazyCommonInputPhone: LazyComponent<typeof import("../components/common/InputPhone.vue")['default']>
|
||||||
|
export const LazyCommonJobView: LazyComponent<typeof import("../components/common/JobView.vue")['default']>
|
||||||
|
export const LazyCommonNoteInfo: LazyComponent<typeof import("../components/common/NoteInfo.vue")['default']>
|
||||||
|
export const LazyCommonNotebox: LazyComponent<typeof import("../components/common/Notebox.vue")['default']>
|
||||||
|
export const LazyCommonPhone: LazyComponent<typeof import("../components/common/Phone.vue")['default']>
|
||||||
|
export const LazyDatatableContextMenu: LazyComponent<typeof import("../components/datatable/ContextMenu.vue")['default']>
|
||||||
|
export const LazyDatatableCreateTemplate: LazyComponent<typeof import("../components/datatable/CreateTemplate.vue")['default']>
|
||||||
|
export const LazyDatatableDataModel: LazyComponent<typeof import("../components/datatable/DataModel.vue")['default']>
|
||||||
|
export const LazyDatatableDataTable: LazyComponent<typeof import("../components/datatable/DataTable.vue")['default']>
|
||||||
|
export const LazyDatatableDataView: LazyComponent<typeof import("../components/datatable/DataView.vue")['default']>
|
||||||
|
export const LazyDatatableEditLabel: LazyComponent<typeof import("../components/datatable/EditLabel.vue")['default']>
|
||||||
|
export const LazyDatatableFieldAttribute: LazyComponent<typeof import("../components/datatable/FieldAttribute.vue")['default']>
|
||||||
|
export const LazyDatatableFilterOption: LazyComponent<typeof import("../components/datatable/FilterOption.vue")['default']>
|
||||||
|
export const LazyDatatableFormatOption: LazyComponent<typeof import("../components/datatable/FormatOption.vue")['default']>
|
||||||
|
export const LazyDatatableMenuSave: LazyComponent<typeof import("../components/datatable/MenuSave.vue")['default']>
|
||||||
|
export const LazyDatatableModelInfo: LazyComponent<typeof import("../components/datatable/ModelInfo.vue")['default']>
|
||||||
|
export const LazyDatatableNewField: LazyComponent<typeof import("../components/datatable/NewField.vue")['default']>
|
||||||
|
export const LazyDatatablePagination: LazyComponent<typeof import("../components/datatable/Pagination.vue")['default']>
|
||||||
|
export const LazyDatatableScrollBox: LazyComponent<typeof import("../components/datatable/ScrollBox.vue")['default']>
|
||||||
|
export const LazyDatatableTableOption: LazyComponent<typeof import("../components/datatable/TableOption.vue")['default']>
|
||||||
|
export const LazyDatatableTableSetting: LazyComponent<typeof import("../components/datatable/TableSetting.vue")['default']>
|
||||||
|
export const LazyDatatableTimeOption: LazyComponent<typeof import("../components/datatable/TimeOption.vue")['default']>
|
||||||
|
export const LazyDatatableFormatColorText: LazyComponent<typeof import("../components/datatable/format/ColorText.vue")['default']>
|
||||||
|
export const LazyDatatableFormatDate: LazyComponent<typeof import("../components/datatable/format/FormatDate.vue")['default']>
|
||||||
|
export const LazyDatatableFormatNumber: LazyComponent<typeof import("../components/datatable/format/FormatNumber.vue")['default']>
|
||||||
|
export const LazyDatatableFormatTime: LazyComponent<typeof import("../components/datatable/format/FormatTime.vue")['default']>
|
||||||
|
export const LazyDatepicker: LazyComponent<typeof import("../components/datepicker/Datepicker.vue")['default']>
|
||||||
|
export const LazyDatepickerEventDetail: LazyComponent<typeof import("../components/datepicker/EventDetail.vue")['default']>
|
||||||
|
export const LazyDatepickerEventMultiMonth: LazyComponent<typeof import("../components/datepicker/EventMultiMonth.vue")['default']>
|
||||||
|
export const LazyDatepickerEventOneMonth: LazyComponent<typeof import("../components/datepicker/EventOneMonth.vue")['default']>
|
||||||
|
export const LazyDatepickerEventSummary: LazyComponent<typeof import("../components/datepicker/EventSummary.vue")['default']>
|
||||||
|
export const LazyDatepickerPickDay: LazyComponent<typeof import("../components/datepicker/PickDay.vue")['default']>
|
||||||
|
export const LazyDatepickerPickMonth: LazyComponent<typeof import("../components/datepicker/PickMonth.vue")['default']>
|
||||||
|
export const LazyDatepickerPickYear: LazyComponent<typeof import("../components/datepicker/PickYear.vue")['default']>
|
||||||
|
export const LazyDatepickerViewEvent: LazyComponent<typeof import("../components/datepicker/ViewEvent.vue")['default']>
|
||||||
|
export const LazyDialogApprovalCode: LazyComponent<typeof import("../components/dialog/ApprovalCode.vue")['default']>
|
||||||
|
export const LazyDialogConfirm: LazyComponent<typeof import("../components/dialog/Confirm.vue")['default']>
|
||||||
|
export const LazyDialogCountDown: LazyComponent<typeof import("../components/dialog/CountDown.vue")['default']>
|
||||||
|
export const LazyDialogDelete: LazyComponent<typeof import("../components/dialog/Delete.vue")['default']>
|
||||||
|
export const LazyDialogError: LazyComponent<typeof import("../components/dialog/Error.vue")['default']>
|
||||||
|
export const LazyDialogInfo: LazyComponent<typeof import("../components/dialog/Info.vue")['default']>
|
||||||
|
export const LazyDialogSuccess: LazyComponent<typeof import("../components/dialog/Success.vue")['default']>
|
||||||
|
export const LazyMaintabConfiguration: LazyComponent<typeof import("../components/maintab/Configuration.vue")['default']>
|
||||||
|
export const LazyMaintabDataDeletion: LazyComponent<typeof import("../components/maintab/DataDeletion.vue")['default']>
|
||||||
|
export const LazyMediaCamera: LazyComponent<typeof import("../components/media/Camera.vue")['default']>
|
||||||
|
export const LazyMediaChipImage: LazyComponent<typeof import("../components/media/ChipImage.vue")['default']>
|
||||||
|
export const LazyMediaCropImage: LazyComponent<typeof import("../components/media/CropImage.vue")['default']>
|
||||||
|
export const LazyMediaFileCount: LazyComponent<typeof import("../components/media/FileCount.vue")['default']>
|
||||||
|
export const LazyMediaFileGallery: LazyComponent<typeof import("../components/media/FileGallery.vue")['default']>
|
||||||
|
export const LazyMediaFileInfo: LazyComponent<typeof import("../components/media/FileInfo.vue")['default']>
|
||||||
|
export const LazyMediaFileList: LazyComponent<typeof import("../components/media/FileList.vue")['default']>
|
||||||
|
export const LazyMediaFileShow: LazyComponent<typeof import("../components/media/FileShow.vue")['default']>
|
||||||
|
export const LazyMediaFileUpload: LazyComponent<typeof import("../components/media/FileUpload.vue")['default']>
|
||||||
|
export const LazyMediaImageAttr: LazyComponent<typeof import("../components/media/ImageAttr.vue")['default']>
|
||||||
|
export const LazyMediaImageGallery: LazyComponent<typeof import("../components/media/ImageGallery.vue")['default']>
|
||||||
|
export const LazyMediaImageShow: LazyComponent<typeof import("../components/media/ImageShow.vue")['default']>
|
||||||
|
export const LazyMediaImagebox: LazyComponent<typeof import("../components/media/Imagebox.vue")['default']>
|
||||||
|
export const LazyMediaPicture: LazyComponent<typeof import("../components/media/Picture.vue")['default']>
|
||||||
|
export const LazyMediaUploadProgress: LazyComponent<typeof import("../components/media/UploadProgress.vue")['default']>
|
||||||
|
export const LazyMediaYoutubeVideo: LazyComponent<typeof import("../components/media/YoutubeVideo.vue")['default']>
|
||||||
|
export const LazyMenuMarkData: LazyComponent<typeof import("../components/menu/MarkData.vue")['default']>
|
||||||
|
export const LazyMenuAction: LazyComponent<typeof import("../components/menu/MenuAction.vue")['default']>
|
||||||
|
export const LazyMenuAdd: LazyComponent<typeof import("../components/menu/MenuAdd.vue")['default']>
|
||||||
|
export const LazyMenuCV: LazyComponent<typeof import("../components/menu/MenuCV.vue")['default']>
|
||||||
|
export const LazyMenuCheck: LazyComponent<typeof import("../components/menu/MenuCheck.vue")['default']>
|
||||||
|
export const LazyMenuCollab: LazyComponent<typeof import("../components/menu/MenuCollab.vue")['default']>
|
||||||
|
export const LazyMenuDocker: LazyComponent<typeof import("../components/menu/MenuDocker.vue")['default']>
|
||||||
|
export const LazyMenuParam: LazyComponent<typeof import("../components/menu/MenuParam.vue")['default']>
|
||||||
|
export const LazyMenuPhone: LazyComponent<typeof import("../components/menu/MenuPhone.vue")['default']>
|
||||||
|
export const LazyMenuRights: LazyComponent<typeof import("../components/menu/MenuRights.vue")['default']>
|
||||||
|
export const LazyMenuStaff: LazyComponent<typeof import("../components/menu/MenuStaff.vue")['default']>
|
||||||
|
export const LazyMenuUser: LazyComponent<typeof import("../components/menu/MenuUser.vue")['default']>
|
||||||
|
export const LazyParameterApps: LazyComponent<typeof import("../components/parameter/Apps.vue")['default']>
|
||||||
|
export const LazyParameterCodeName: LazyComponent<typeof import("../components/parameter/CodeName.vue")['default']>
|
||||||
|
export const LazyParameterCommon: LazyComponent<typeof import("../components/parameter/Common.vue")['default']>
|
||||||
|
export const LazyParameterDataSetting: LazyComponent<typeof import("../components/parameter/DataSetting.vue")['default']>
|
||||||
|
export const LazyParameterEmailSetup: LazyComponent<typeof import("../components/parameter/EmailSetup.vue")['default']>
|
||||||
|
export const LazyParameterNameDetail: LazyComponent<typeof import("../components/parameter/NameDetail.vue")['default']>
|
||||||
|
export const LazyParameterSettingFields: LazyComponent<typeof import("../components/parameter/SettingFields.vue")['default']>
|
||||||
|
export const LazyServerContainerMgnt: LazyComponent<typeof import("../components/server/ContainerMgnt.vue")['default']>
|
||||||
|
export const LazyServerCpuRam: LazyComponent<typeof import("../components/server/CpuRam.vue")['default']>
|
||||||
|
export const LazyServerDiskInfo: LazyComponent<typeof import("../components/server/DiskInfo.vue")['default']>
|
||||||
|
export const LazyServerDockerContainer: LazyComponent<typeof import("../components/server/DockerContainer.vue")['default']>
|
||||||
|
export const LazyServerInfo: LazyComponent<typeof import("../components/server/ServerInfo.vue")['default']>
|
||||||
|
export const LazySnackbarError: LazyComponent<typeof import("../components/snackbar/Error.vue")['default']>
|
||||||
|
export const LazySnackbarInfo: LazyComponent<typeof import("../components/snackbar/Info.vue")['default']>
|
||||||
|
export const LazySnackbarSnackBar: LazyComponent<typeof import("../components/snackbar/SnackBar.vue")['default']>
|
||||||
|
export const LazySnackbarSuccess: LazyComponent<typeof import("../components/snackbar/Success.vue")['default']>
|
||||||
|
export const LazyUserChangePass: LazyComponent<typeof import("../components/user/ChangePass.vue")['default']>
|
||||||
|
export const LazyUserConfirmCode: LazyComponent<typeof import("../components/user/ConfirmCode.vue")['default']>
|
||||||
|
export const LazyUserCreateUser: LazyComponent<typeof import("../components/user/CreateUser.vue")['default']>
|
||||||
|
export const LazyUserEditUser: LazyComponent<typeof import("../components/user/EditUser.vue")['default']>
|
||||||
|
export const LazyUserLogout: LazyComponent<typeof import("../components/user/Logout.vue")['default']>
|
||||||
|
export const LazyUserNewUser: LazyComponent<typeof import("../components/user/NewUser.vue")['default']>
|
||||||
|
export const LazyUserProfile: LazyComponent<typeof import("../components/user/Profile.vue")['default']>
|
||||||
|
export const LazyUserSetPassword: LazyComponent<typeof import("../components/user/SetPassword.vue")['default']>
|
||||||
|
export const LazyUserInfo: LazyComponent<typeof import("../components/user/UserInfo.vue")['default']>
|
||||||
|
export const LazyNuxtWelcome: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/welcome.vue")['default']>
|
||||||
|
export const LazyNuxtLayout: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-layout")['default']>
|
||||||
|
export const LazyNuxtErrorBoundary: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue")['default']>
|
||||||
|
export const LazyClientOnly: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/client-only")['default']>
|
||||||
|
export const LazyDevOnly: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/dev-only")['default']>
|
||||||
|
export const LazyServerPlaceholder: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/server-placeholder")['default']>
|
||||||
|
export const LazyNuxtLink: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-link")['default']>
|
||||||
|
export const LazyNuxtLoadingIndicator: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-loading-indicator")['default']>
|
||||||
|
export const LazyNuxtTime: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-time.vue")['default']>
|
||||||
|
export const LazyNuxtRouteAnnouncer: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-route-announcer")['default']>
|
||||||
|
export const LazyNuxtImg: LazyComponent<typeof import("../node_modules/@nuxt/image/dist/runtime/components/NuxtImg.vue")['default']>
|
||||||
|
export const LazyNuxtPicture: LazyComponent<typeof import("../node_modules/@nuxt/image/dist/runtime/components/NuxtPicture.vue")['default']>
|
||||||
|
export const LazyNuxtPage: LazyComponent<typeof import("../node_modules/nuxt/dist/pages/runtime/page")['default']>
|
||||||
|
export const LazyNoScript: LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['NoScript']>
|
||||||
|
export const LazyLink: LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Link']>
|
||||||
|
export const LazyBase: LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Base']>
|
||||||
|
export const LazyTitle: LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Title']>
|
||||||
|
export const LazyMeta: LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Meta']>
|
||||||
|
export const LazyStyle: LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Style']>
|
||||||
|
export const LazyHead: LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Head']>
|
||||||
|
export const LazyHtml: LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Html']>
|
||||||
|
export const LazyBody: LazyComponent<typeof import("../node_modules/nuxt/dist/head/runtime/components")['Body']>
|
||||||
|
export const LazyNuxtIsland: LazyComponent<typeof import("../node_modules/nuxt/dist/app/components/nuxt-island")['default']>
|
||||||
|
export const LazyNuxtRouteAnnouncer: LazyComponent<IslandComponent<typeof import("../node_modules/nuxt/dist/app/components/server-placeholder")['default']>>
|
||||||
|
|
||||||
|
export const componentNames: string[]
|
||||||
1989
.nuxt/dev/index.mjs
Normal file
1989
.nuxt/dev/index.mjs
Normal file
File diff suppressed because it is too large
Load Diff
1
.nuxt/dev/index.mjs.map
Normal file
1
.nuxt/dev/index.mjs.map
Normal file
File diff suppressed because one or more lines are too long
18
.nuxt/dist/server/client.manifest.json
vendored
Normal file
18
.nuxt/dist/server/client.manifest.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"@vite/client": {
|
||||||
|
"prefetch": true,
|
||||||
|
"isEntry": true,
|
||||||
|
"file": "@vite/client",
|
||||||
|
"css": [],
|
||||||
|
"module": true,
|
||||||
|
"resourceType": "script"
|
||||||
|
},
|
||||||
|
"/home/loitx/dev/utopia/system/node_modules/nuxt/dist/app/entry.js": {
|
||||||
|
"resourceType": "script",
|
||||||
|
"module": true,
|
||||||
|
"prefetch": true,
|
||||||
|
"preload": true,
|
||||||
|
"isEntry": true,
|
||||||
|
"file": "/home/loitx/dev/utopia/system/node_modules/nuxt/dist/app/entry.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
.nuxt/dist/server/client.manifest.mjs
vendored
Normal file
1
.nuxt/dist/server/client.manifest.mjs
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "file:///home/loitx/dev/utopia/system/node_modules/@nuxt/vite-builder/dist/runtime/client.manifest.mjs"
|
||||||
1
.nuxt/dist/server/server.mjs
vendored
Normal file
1
.nuxt/dist/server/server.mjs
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "file:///home/loitx/dev/utopia/system/node_modules/@nuxt/vite-builder/dist/runtime/vite-node.mjs"
|
||||||
36
.nuxt/imports.d.ts
vendored
Normal file
36
.nuxt/imports.d.ts
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
export { useScriptTriggerConsent, useScriptEventPage, useScriptTriggerElement, useScript, useScriptGoogleAnalytics, useScriptPlausibleAnalytics, useScriptCrisp, useScriptClarity, useScriptCloudflareWebAnalytics, useScriptFathomAnalytics, useScriptMatomoAnalytics, useScriptGoogleTagManager, useScriptGoogleAdsense, useScriptSegment, useScriptMetaPixel, useScriptXPixel, useScriptIntercom, useScriptHotjar, useScriptStripe, useScriptLemonSqueezy, useScriptVimeoPlayer, useScriptYouTubePlayer, useScriptGoogleMaps, useScriptNpm, useScriptUmamiAnalytics, useScriptSnapchatPixel } from '#app/composables/script-stubs';
|
||||||
|
export { isVue2, isVue3 } from 'vue-demi';
|
||||||
|
export { defineNuxtLink } from '#app/components/nuxt-link';
|
||||||
|
export { useNuxtApp, tryUseNuxtApp, defineNuxtPlugin, definePayloadPlugin, useRuntimeConfig, defineAppConfig } from '#app/nuxt';
|
||||||
|
export { useAppConfig, updateAppConfig } from '#app/config';
|
||||||
|
export { defineNuxtComponent } from '#app/composables/component';
|
||||||
|
export { useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData } from '#app/composables/asyncData';
|
||||||
|
export { useHydration } from '#app/composables/hydrate';
|
||||||
|
export { callOnce } from '#app/composables/once';
|
||||||
|
export { useState, clearNuxtState } from '#app/composables/state';
|
||||||
|
export { clearError, createError, isNuxtError, showError, useError } from '#app/composables/error';
|
||||||
|
export { useFetch, useLazyFetch } from '#app/composables/fetch';
|
||||||
|
export { useCookie, refreshCookie } from '#app/composables/cookie';
|
||||||
|
export { onPrehydrate, prerenderRoutes, useRequestHeader, useRequestHeaders, useResponseHeader, useRequestEvent, useRequestFetch, setResponseStatus } from '#app/composables/ssr';
|
||||||
|
export { onNuxtReady } from '#app/composables/ready';
|
||||||
|
export { preloadComponents, prefetchComponents, preloadRouteComponents } from '#app/composables/preload';
|
||||||
|
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useRouter } from '#app/composables/router';
|
||||||
|
export { isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver } from '#app/composables/payload';
|
||||||
|
export { useLoadingIndicator } from '#app/composables/loading-indicator';
|
||||||
|
export { getAppManifest, getRouteRules } from '#app/composables/manifest';
|
||||||
|
export { reloadNuxtApp } from '#app/composables/chunk';
|
||||||
|
export { useRequestURL } from '#app/composables/url';
|
||||||
|
export { usePreviewMode } from '#app/composables/preview';
|
||||||
|
export { useRouteAnnouncer } from '#app/composables/route-announcer';
|
||||||
|
export { useRuntimeHook } from '#app/composables/runtime-hook';
|
||||||
|
export { useHead, useHeadSafe, useServerHeadSafe, useServerHead, useSeoMeta, useServerSeoMeta, injectHead } from '#app/composables/head';
|
||||||
|
export { onBeforeRouteLeave, onBeforeRouteUpdate, useLink } from 'vue-router';
|
||||||
|
export { withCtx, withDirectives, withKeys, withMemo, withModifiers, withScopeId, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onServerPrefetch, onUnmounted, onUpdated, computed, customRef, isProxy, isReactive, isReadonly, isRef, markRaw, proxyRefs, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs, triggerRef, unref, watch, watchEffect, watchPostEffect, watchSyncEffect, isShallow, effect, effectScope, getCurrentScope, onScopeDispose, defineComponent, defineAsyncComponent, resolveComponent, getCurrentInstance, h, inject, hasInjectionContext, nextTick, provide, mergeModels, toValue, useModel, useAttrs, useCssModule, useCssVars, useSlots, useTransitionState, useId, useTemplateRef, useShadowRoot, Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue';
|
||||||
|
export { requestIdleCallback, cancelIdleCallback } from '#app/compat/idle-callback';
|
||||||
|
export { setInterval } from '#app/compat/interval';
|
||||||
|
export { useStore } from '../stores/index';
|
||||||
|
export { defineStore, acceptHMRUpdate, usePinia, storeToRefs } from '../node_modules/@pinia/nuxt/dist/runtime/composables';
|
||||||
|
export { useImage } from '../node_modules/@nuxt/image/dist/runtime/composables';
|
||||||
|
export { storages as piniaPluginPersistedstate } from '../node_modules/pinia-plugin-persistedstate/dist/nuxt/runtime/storages';
|
||||||
|
export { useNuxtDevTools } from '../node_modules/@nuxt/devtools/dist/runtime/use-nuxt-devtools';
|
||||||
|
export { definePageMeta } from '../node_modules/nuxt/dist/pages/runtime/composables';
|
||||||
1
.nuxt/manifest/latest.json
Normal file
1
.nuxt/manifest/latest.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"id":"dev","timestamp":1767953360905}
|
||||||
1
.nuxt/manifest/meta/dev.json
Normal file
1
.nuxt/manifest/meta/dev.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"id":"dev","timestamp":1767953360905,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|
||||||
17
.nuxt/nitro.json
Normal file
17
.nuxt/nitro.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-01-09T10:09:26.908Z",
|
||||||
|
"preset": "nitro-dev",
|
||||||
|
"framework": {
|
||||||
|
"name": "nuxt",
|
||||||
|
"version": "3.17.3"
|
||||||
|
},
|
||||||
|
"versions": {
|
||||||
|
"nitro": "2.11.12"
|
||||||
|
},
|
||||||
|
"dev": {
|
||||||
|
"pid": 6871,
|
||||||
|
"workerAddress": {
|
||||||
|
"socketPath": "\u0000nitro-worker-6871-1-1-5503.sock"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
.nuxt/nuxt.d.ts
vendored
Normal file
25
.nuxt/nuxt.d.ts
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Generated by nuxi
|
||||||
|
/// <reference types="@pinia/nuxt" />
|
||||||
|
/// <reference types="@nuxt/image" />
|
||||||
|
/// <reference types="@nuxt/telemetry" />
|
||||||
|
/// <reference types="@nuxt/devtools" />
|
||||||
|
/// <reference types="pinia-plugin-persistedstate" />
|
||||||
|
/// <reference path="types/builder-env.d.ts" />
|
||||||
|
/// <reference types="nuxt" />
|
||||||
|
/// <reference path="types/app-defaults.d.ts" />
|
||||||
|
/// <reference path="types/plugins.d.ts" />
|
||||||
|
/// <reference path="types/build.d.ts" />
|
||||||
|
/// <reference path="types/schema.d.ts" />
|
||||||
|
/// <reference path="types/app.config.d.ts" />
|
||||||
|
/// <reference types="@pinia/nuxt" />
|
||||||
|
/// <reference types="vue-router" />
|
||||||
|
/// <reference path="types/middleware.d.ts" />
|
||||||
|
/// <reference path="types/nitro-middleware.d.ts" />
|
||||||
|
/// <reference path="types/layouts.d.ts" />
|
||||||
|
/// <reference path="components.d.ts" />
|
||||||
|
/// <reference path="imports.d.ts" />
|
||||||
|
/// <reference path="types/imports.d.ts" />
|
||||||
|
/// <reference path="schema/nuxt.schema.d.ts" />
|
||||||
|
/// <reference path="types/nitro.d.ts" />
|
||||||
|
|
||||||
|
export {}
|
||||||
9
.nuxt/nuxt.json
Normal file
9
.nuxt/nuxt.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"_hash": "iqGp068BWfiAu_k2dQDM8pZ97MTfxOPxasPvGUynqSY",
|
||||||
|
"project": {
|
||||||
|
"rootDir": "/home/loitx/dev/utopia/system"
|
||||||
|
},
|
||||||
|
"versions": {
|
||||||
|
"nuxt": "3.17.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
17
.nuxt/schema/nuxt.schema.d.ts
vendored
Normal file
17
.nuxt/schema/nuxt.schema.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export interface NuxtCustomSchema {
|
||||||
|
|
||||||
|
}
|
||||||
|
export type CustomAppConfig = Exclude<NuxtCustomSchema['appConfig'], undefined>
|
||||||
|
type _CustomAppConfig = CustomAppConfig
|
||||||
|
|
||||||
|
declare module '@nuxt/schema' {
|
||||||
|
interface NuxtConfig extends Omit<NuxtCustomSchema, 'appConfig'> {}
|
||||||
|
interface NuxtOptions extends Omit<NuxtCustomSchema, 'appConfig'> {}
|
||||||
|
interface CustomAppConfig extends _CustomAppConfig {}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'nuxt/schema' {
|
||||||
|
interface NuxtConfig extends Omit<NuxtCustomSchema, 'appConfig'> {}
|
||||||
|
interface NuxtOptions extends Omit<NuxtCustomSchema, 'appConfig'> {}
|
||||||
|
interface CustomAppConfig extends _CustomAppConfig {}
|
||||||
|
}
|
||||||
3
.nuxt/schema/nuxt.schema.json
Normal file
3
.nuxt/schema/nuxt.schema.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"id": "#"
|
||||||
|
}
|
||||||
192
.nuxt/tsconfig.json
Normal file
192
.nuxt/tsconfig.json
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
// Generated by nuxi
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"nitropack/types": [
|
||||||
|
"../node_modules/nitropack/types"
|
||||||
|
],
|
||||||
|
"nitropack/runtime": [
|
||||||
|
"../node_modules/nitropack/runtime"
|
||||||
|
],
|
||||||
|
"nitropack": [
|
||||||
|
"../node_modules/nitropack"
|
||||||
|
],
|
||||||
|
"defu": [
|
||||||
|
"../node_modules/defu"
|
||||||
|
],
|
||||||
|
"h3": [
|
||||||
|
"../node_modules/h3"
|
||||||
|
],
|
||||||
|
"consola": [
|
||||||
|
"../node_modules/consola"
|
||||||
|
],
|
||||||
|
"ofetch": [
|
||||||
|
"../node_modules/ofetch"
|
||||||
|
],
|
||||||
|
"@unhead/vue": [
|
||||||
|
"../node_modules/@unhead/vue"
|
||||||
|
],
|
||||||
|
"@nuxt/devtools": [
|
||||||
|
"../node_modules/@nuxt/devtools"
|
||||||
|
],
|
||||||
|
"@vue/runtime-core": [
|
||||||
|
"../node_modules/@vue/runtime-core"
|
||||||
|
],
|
||||||
|
"@vue/compiler-sfc": [
|
||||||
|
"../node_modules/@vue/compiler-sfc"
|
||||||
|
],
|
||||||
|
"unplugin-vue-router/client": [
|
||||||
|
"../node_modules/unplugin-vue-router/client"
|
||||||
|
],
|
||||||
|
"@nuxt/schema": [
|
||||||
|
"../node_modules/@nuxt/schema"
|
||||||
|
],
|
||||||
|
"nuxt": [
|
||||||
|
"../node_modules/nuxt"
|
||||||
|
],
|
||||||
|
"vite/client": [
|
||||||
|
"../node_modules/vite/client"
|
||||||
|
],
|
||||||
|
"~": [
|
||||||
|
".."
|
||||||
|
],
|
||||||
|
"~/*": [
|
||||||
|
"../*"
|
||||||
|
],
|
||||||
|
"@": [
|
||||||
|
".."
|
||||||
|
],
|
||||||
|
"@/*": [
|
||||||
|
"../*"
|
||||||
|
],
|
||||||
|
"~~": [
|
||||||
|
".."
|
||||||
|
],
|
||||||
|
"~~/*": [
|
||||||
|
"../*"
|
||||||
|
],
|
||||||
|
"@@": [
|
||||||
|
".."
|
||||||
|
],
|
||||||
|
"@@/*": [
|
||||||
|
"../*"
|
||||||
|
],
|
||||||
|
"#shared": [
|
||||||
|
"../shared"
|
||||||
|
],
|
||||||
|
"assets": [
|
||||||
|
"../assets"
|
||||||
|
],
|
||||||
|
"assets/*": [
|
||||||
|
"../assets/*"
|
||||||
|
],
|
||||||
|
"public": [
|
||||||
|
"../public"
|
||||||
|
],
|
||||||
|
"public/*": [
|
||||||
|
"../public/*"
|
||||||
|
],
|
||||||
|
"#app": [
|
||||||
|
"../node_modules/nuxt/dist/app"
|
||||||
|
],
|
||||||
|
"#app/*": [
|
||||||
|
"../node_modules/nuxt/dist/app/*"
|
||||||
|
],
|
||||||
|
"vue-demi": [
|
||||||
|
"../node_modules/nuxt/dist/app/compat/vue-demi"
|
||||||
|
],
|
||||||
|
"#image": [
|
||||||
|
"../node_modules/@nuxt/image/dist/runtime"
|
||||||
|
],
|
||||||
|
"#image/*": [
|
||||||
|
"../node_modules/@nuxt/image/dist/runtime/*"
|
||||||
|
],
|
||||||
|
"#vue-router": [
|
||||||
|
"../node_modules/vue-router"
|
||||||
|
],
|
||||||
|
"#unhead/composables": [
|
||||||
|
"../node_modules/nuxt/dist/head/runtime/composables/v3"
|
||||||
|
],
|
||||||
|
"#imports": [
|
||||||
|
"./imports"
|
||||||
|
],
|
||||||
|
"#app-manifest": [
|
||||||
|
"./manifest/meta/dev"
|
||||||
|
],
|
||||||
|
"#components": [
|
||||||
|
"./components"
|
||||||
|
],
|
||||||
|
"#build": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"#build/*": [
|
||||||
|
"./*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"allowJs": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"isolatedModules": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"strict": true,
|
||||||
|
"noUncheckedIndexedAccess": false,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"module": "preserve",
|
||||||
|
"noEmit": true,
|
||||||
|
"lib": [
|
||||||
|
"ESNext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"webworker"
|
||||||
|
],
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxImportSource": "vue",
|
||||||
|
"types": [],
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"../**/*",
|
||||||
|
"../.config/nuxt.*",
|
||||||
|
"./nuxt.d.ts",
|
||||||
|
"../node_modules/@pinia/nuxt/runtime",
|
||||||
|
"../node_modules/@pinia/nuxt/dist/runtime",
|
||||||
|
"../node_modules/@nuxt/image/runtime",
|
||||||
|
"../node_modules/@nuxt/image/dist/runtime",
|
||||||
|
"../n/runtime",
|
||||||
|
"../n/dist/runtime",
|
||||||
|
"../node_modules/@nuxt/devtools/runtime",
|
||||||
|
"../node_modules/@nuxt/devtools/dist/runtime",
|
||||||
|
"../node_modules/@nuxt/telemetry/runtime",
|
||||||
|
"../node_modules/@nuxt/telemetry/dist/runtime",
|
||||||
|
".."
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"../dist",
|
||||||
|
"../.data",
|
||||||
|
"../node_modules",
|
||||||
|
"../node_modules/nuxt/node_modules",
|
||||||
|
"../node_modules/@pinia/nuxt/node_modules",
|
||||||
|
"../node_modules/@nuxt/image/node_modules",
|
||||||
|
"../node_modules/pinia-plugin-persistedstate/node_modules",
|
||||||
|
"../node_modules/@nuxt/devtools/node_modules",
|
||||||
|
"../node_modules/@nuxt/telemetry/node_modules",
|
||||||
|
"../node_modules/@pinia/nuxt/runtime/server",
|
||||||
|
"../node_modules/@pinia/nuxt/dist/runtime/server",
|
||||||
|
"../node_modules/@nuxt/image/runtime/server",
|
||||||
|
"../node_modules/@nuxt/image/dist/runtime/server",
|
||||||
|
"../n/runtime/server",
|
||||||
|
"../n/dist/runtime/server",
|
||||||
|
"../node_modules/@nuxt/devtools/runtime/server",
|
||||||
|
"../node_modules/@nuxt/devtools/dist/runtime/server",
|
||||||
|
"../node_modules/@nuxt/telemetry/runtime/server",
|
||||||
|
"../node_modules/@nuxt/telemetry/dist/runtime/server",
|
||||||
|
"../.output"
|
||||||
|
]
|
||||||
|
}
|
||||||
137
.nuxt/tsconfig.server.json
Normal file
137
.nuxt/tsconfig.server.json
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"allowJs": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"jsxFactory": "h",
|
||||||
|
"jsxFragmentFactory": "Fragment",
|
||||||
|
"paths": {
|
||||||
|
"#imports": [
|
||||||
|
"./types/nitro-imports"
|
||||||
|
],
|
||||||
|
"~/*": [
|
||||||
|
"../*"
|
||||||
|
],
|
||||||
|
"@/*": [
|
||||||
|
"../*"
|
||||||
|
],
|
||||||
|
"~~/*": [
|
||||||
|
"../*"
|
||||||
|
],
|
||||||
|
"@@/*": [
|
||||||
|
"../*"
|
||||||
|
],
|
||||||
|
"nitropack/types": [
|
||||||
|
"../node_modules/nitropack/types"
|
||||||
|
],
|
||||||
|
"nitropack/runtime": [
|
||||||
|
"../node_modules/nitropack/runtime"
|
||||||
|
],
|
||||||
|
"nitropack": [
|
||||||
|
"../node_modules/nitropack"
|
||||||
|
],
|
||||||
|
"defu": [
|
||||||
|
"../node_modules/defu"
|
||||||
|
],
|
||||||
|
"h3": [
|
||||||
|
"../node_modules/h3"
|
||||||
|
],
|
||||||
|
"consola": [
|
||||||
|
"../node_modules/consola"
|
||||||
|
],
|
||||||
|
"ofetch": [
|
||||||
|
"../node_modules/ofetch"
|
||||||
|
],
|
||||||
|
"@unhead/vue": [
|
||||||
|
"../node_modules/@unhead/vue"
|
||||||
|
],
|
||||||
|
"@nuxt/devtools": [
|
||||||
|
"../node_modules/@nuxt/devtools"
|
||||||
|
],
|
||||||
|
"@vue/runtime-core": [
|
||||||
|
"../node_modules/@vue/runtime-core"
|
||||||
|
],
|
||||||
|
"@vue/compiler-sfc": [
|
||||||
|
"../node_modules/@vue/compiler-sfc"
|
||||||
|
],
|
||||||
|
"unplugin-vue-router/client": [
|
||||||
|
"../node_modules/unplugin-vue-router/client"
|
||||||
|
],
|
||||||
|
"@nuxt/schema": [
|
||||||
|
"../node_modules/@nuxt/schema"
|
||||||
|
],
|
||||||
|
"nuxt": [
|
||||||
|
"../node_modules/nuxt"
|
||||||
|
],
|
||||||
|
"vite/client": [
|
||||||
|
"../node_modules/vite/client"
|
||||||
|
],
|
||||||
|
"#shared": [
|
||||||
|
"../shared"
|
||||||
|
],
|
||||||
|
"assets": [
|
||||||
|
"../assets"
|
||||||
|
],
|
||||||
|
"assets/*": [
|
||||||
|
"../assets/*"
|
||||||
|
],
|
||||||
|
"public": [
|
||||||
|
"../public"
|
||||||
|
],
|
||||||
|
"public/*": [
|
||||||
|
"../public/*"
|
||||||
|
],
|
||||||
|
"#build": [
|
||||||
|
"./"
|
||||||
|
],
|
||||||
|
"#build/*": [
|
||||||
|
"./*"
|
||||||
|
],
|
||||||
|
"#internal/nuxt/paths": [
|
||||||
|
"../node_modules/nuxt/dist/core/runtime/nitro/utils/paths"
|
||||||
|
],
|
||||||
|
"#image": [
|
||||||
|
"../node_modules/@nuxt/image/dist/runtime"
|
||||||
|
],
|
||||||
|
"#image/*": [
|
||||||
|
"../node_modules/@nuxt/image/dist/runtime/*"
|
||||||
|
],
|
||||||
|
"#unhead/composables": [
|
||||||
|
"../node_modules/nuxt/dist/head/runtime/composables/v3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"webworker",
|
||||||
|
"dom.iterable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./types/nitro-nuxt.d.ts",
|
||||||
|
"../node_modules/@pinia/nuxt/runtime/server",
|
||||||
|
"../node_modules/@nuxt/image/runtime/server",
|
||||||
|
"../n/runtime/server",
|
||||||
|
"../node_modules/@nuxt/devtools/runtime/server",
|
||||||
|
"../node_modules/@nuxt/telemetry/runtime/server",
|
||||||
|
"./types/nitro.d.ts",
|
||||||
|
"../**/*",
|
||||||
|
"../server/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"../node_modules",
|
||||||
|
"../node_modules/nuxt/node_modules",
|
||||||
|
"../node_modules/@pinia/nuxt/node_modules",
|
||||||
|
"../node_modules/@nuxt/image/node_modules",
|
||||||
|
"../node_modules/pinia-plugin-persistedstate/node_modules",
|
||||||
|
"../node_modules/@nuxt/devtools/node_modules",
|
||||||
|
"../node_modules/@nuxt/telemetry/node_modules",
|
||||||
|
"../dist"
|
||||||
|
]
|
||||||
|
}
|
||||||
7
.nuxt/types/app-defaults.d.ts
vendored
Normal file
7
.nuxt/types/app-defaults.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
declare module 'nuxt/app/defaults' {
|
||||||
|
type DefaultAsyncDataErrorValue = null
|
||||||
|
type DefaultAsyncDataValue = null
|
||||||
|
type DefaultErrorValue = null
|
||||||
|
type DedupeOption = boolean | 'cancel' | 'defer'
|
||||||
|
}
|
||||||
31
.nuxt/types/app.config.d.ts
vendored
Normal file
31
.nuxt/types/app.config.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
import type { CustomAppConfig } from 'nuxt/schema'
|
||||||
|
import type { Defu } from 'defu'
|
||||||
|
|
||||||
|
|
||||||
|
declare const inlineConfig = {
|
||||||
|
"nuxt": {}
|
||||||
|
}
|
||||||
|
type ResolvedAppConfig = Defu<typeof inlineConfig, []>
|
||||||
|
type IsAny<T> = 0 extends 1 & T ? true : false
|
||||||
|
|
||||||
|
type MergedAppConfig<Resolved extends Record<string, unknown>, Custom extends Record<string, unknown>> = {
|
||||||
|
[K in keyof (Resolved & Custom)]: K extends keyof Custom
|
||||||
|
? unknown extends Custom[K]
|
||||||
|
? Resolved[K]
|
||||||
|
: IsAny<Custom[K]> extends true
|
||||||
|
? Resolved[K]
|
||||||
|
: Custom[K] extends Record<string, any>
|
||||||
|
? Resolved[K] extends Record<string, any>
|
||||||
|
? MergedAppConfig<Resolved[K], Custom[K]>
|
||||||
|
: Exclude<Custom[K], undefined>
|
||||||
|
: Exclude<Custom[K], undefined>
|
||||||
|
: Resolved[K]
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'nuxt/schema' {
|
||||||
|
interface AppConfig extends MergedAppConfig<ResolvedAppConfig, CustomAppConfig> { }
|
||||||
|
}
|
||||||
|
declare module '@nuxt/schema' {
|
||||||
|
interface AppConfig extends MergedAppConfig<ResolvedAppConfig, CustomAppConfig> { }
|
||||||
|
}
|
||||||
24
.nuxt/types/build.d.ts
vendored
Normal file
24
.nuxt/types/build.d.ts
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
declare module "#build/app-component.mjs";
|
||||||
|
declare module "#build/nitro.client.mjs";
|
||||||
|
declare module "#build/plugins.client.mjs";
|
||||||
|
declare module "#build/css.mjs";
|
||||||
|
declare module "#build/fetch.mjs";
|
||||||
|
declare module "#build/error-component.mjs";
|
||||||
|
declare module "#build/layouts.mjs";
|
||||||
|
declare module "#build/middleware.mjs";
|
||||||
|
declare module "#build/nuxt.config.mjs";
|
||||||
|
declare module "#build/paths.mjs";
|
||||||
|
declare module "#build/root-component.mjs";
|
||||||
|
declare module "#build/plugins.server.mjs";
|
||||||
|
declare module "#build/test-component-wrapper.mjs";
|
||||||
|
declare module "#build/image-options.mjs";
|
||||||
|
declare module "#build/devtools/settings.mjs";
|
||||||
|
declare module "#build/runtime.vue-devtools-client.7MLPiAuXoif6XsIY8CZEwf71-YdXciZa7od8VgMYwaY.js";
|
||||||
|
declare module "#build/routes.mjs";
|
||||||
|
declare module "#build/pages.mjs";
|
||||||
|
declare module "#build/router.options.mjs";
|
||||||
|
declare module "#build/unhead-options.mjs";
|
||||||
|
declare module "#build/unhead.config.mjs";
|
||||||
|
declare module "#build/components.plugin.mjs";
|
||||||
|
declare module "#build/component-names.mjs";
|
||||||
|
declare module "#build/components.islands.mjs";
|
||||||
1
.nuxt/types/builder-env.d.ts
vendored
Normal file
1
.nuxt/types/builder-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import "vite/client";
|
||||||
368
.nuxt/types/imports.d.ts
vendored
Normal file
368
.nuxt/types/imports.d.ts
vendored
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
// Generated by auto imports
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const abortNavigation: typeof import('../../node_modules/nuxt/dist/app/composables/router')['abortNavigation']
|
||||||
|
const acceptHMRUpdate: typeof import('../../node_modules/@pinia/nuxt/dist/runtime/composables')['acceptHMRUpdate']
|
||||||
|
const addRouteMiddleware: typeof import('../../node_modules/nuxt/dist/app/composables/router')['addRouteMiddleware']
|
||||||
|
const callOnce: typeof import('../../node_modules/nuxt/dist/app/composables/once')['callOnce']
|
||||||
|
const cancelIdleCallback: typeof import('../../node_modules/nuxt/dist/app/compat/idle-callback')['cancelIdleCallback']
|
||||||
|
const clearError: typeof import('../../node_modules/nuxt/dist/app/composables/error')['clearError']
|
||||||
|
const clearNuxtData: typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['clearNuxtData']
|
||||||
|
const clearNuxtState: typeof import('../../node_modules/nuxt/dist/app/composables/state')['clearNuxtState']
|
||||||
|
const computed: typeof import('vue')['computed']
|
||||||
|
const createError: typeof import('../../node_modules/nuxt/dist/app/composables/error')['createError']
|
||||||
|
const customRef: typeof import('vue')['customRef']
|
||||||
|
const defineAppConfig: typeof import('../../node_modules/nuxt/dist/app/nuxt')['defineAppConfig']
|
||||||
|
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||||
|
const defineComponent: typeof import('vue')['defineComponent']
|
||||||
|
const defineNuxtComponent: typeof import('../../node_modules/nuxt/dist/app/composables/component')['defineNuxtComponent']
|
||||||
|
const defineNuxtLink: typeof import('../../node_modules/nuxt/dist/app/components/nuxt-link')['defineNuxtLink']
|
||||||
|
const defineNuxtPlugin: typeof import('../../node_modules/nuxt/dist/app/nuxt')['defineNuxtPlugin']
|
||||||
|
const defineNuxtRouteMiddleware: typeof import('../../node_modules/nuxt/dist/app/composables/router')['defineNuxtRouteMiddleware']
|
||||||
|
const definePageMeta: typeof import('../../node_modules/nuxt/dist/pages/runtime/composables')['definePageMeta']
|
||||||
|
const definePayloadPlugin: typeof import('../../node_modules/nuxt/dist/app/nuxt')['definePayloadPlugin']
|
||||||
|
const definePayloadReducer: typeof import('../../node_modules/nuxt/dist/app/composables/payload')['definePayloadReducer']
|
||||||
|
const definePayloadReviver: typeof import('../../node_modules/nuxt/dist/app/composables/payload')['definePayloadReviver']
|
||||||
|
const defineStore: typeof import('../../node_modules/@pinia/nuxt/dist/runtime/composables')['defineStore']
|
||||||
|
const effect: typeof import('vue')['effect']
|
||||||
|
const effectScope: typeof import('vue')['effectScope']
|
||||||
|
const getAppManifest: typeof import('../../node_modules/nuxt/dist/app/composables/manifest')['getAppManifest']
|
||||||
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
|
const getRouteRules: typeof import('../../node_modules/nuxt/dist/app/composables/manifest')['getRouteRules']
|
||||||
|
const h: typeof import('vue')['h']
|
||||||
|
const hasInjectionContext: typeof import('vue')['hasInjectionContext']
|
||||||
|
const inject: typeof import('vue')['inject']
|
||||||
|
const injectHead: typeof import('../../node_modules/nuxt/dist/app/composables/head')['injectHead']
|
||||||
|
const isNuxtError: typeof import('../../node_modules/nuxt/dist/app/composables/error')['isNuxtError']
|
||||||
|
const isPrerendered: typeof import('../../node_modules/nuxt/dist/app/composables/payload')['isPrerendered']
|
||||||
|
const isProxy: typeof import('vue')['isProxy']
|
||||||
|
const isReactive: typeof import('vue')['isReactive']
|
||||||
|
const isReadonly: typeof import('vue')['isReadonly']
|
||||||
|
const isRef: typeof import('vue')['isRef']
|
||||||
|
const isShallow: typeof import('vue')['isShallow']
|
||||||
|
const isVue2: typeof import('../../node_modules/nuxt/dist/app/compat/vue-demi')['isVue2']
|
||||||
|
const isVue3: typeof import('../../node_modules/nuxt/dist/app/compat/vue-demi')['isVue3']
|
||||||
|
const loadPayload: typeof import('../../node_modules/nuxt/dist/app/composables/payload')['loadPayload']
|
||||||
|
const markRaw: typeof import('vue')['markRaw']
|
||||||
|
const mergeModels: typeof import('vue')['mergeModels']
|
||||||
|
const navigateTo: typeof import('../../node_modules/nuxt/dist/app/composables/router')['navigateTo']
|
||||||
|
const nextTick: typeof import('vue')['nextTick']
|
||||||
|
const onActivated: typeof import('vue')['onActivated']
|
||||||
|
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||||
|
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
|
||||||
|
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
|
||||||
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||||
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||||
|
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||||
|
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||||
|
const onMounted: typeof import('vue')['onMounted']
|
||||||
|
const onNuxtReady: typeof import('../../node_modules/nuxt/dist/app/composables/ready')['onNuxtReady']
|
||||||
|
const onPrehydrate: typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['onPrehydrate']
|
||||||
|
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||||
|
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||||
|
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||||
|
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||||
|
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||||
|
const onUpdated: typeof import('vue')['onUpdated']
|
||||||
|
const piniaPluginPersistedstate: typeof import('../../node_modules/pinia-plugin-persistedstate/dist/nuxt/runtime/storages')['storages']
|
||||||
|
const prefetchComponents: typeof import('../../node_modules/nuxt/dist/app/composables/preload')['prefetchComponents']
|
||||||
|
const preloadComponents: typeof import('../../node_modules/nuxt/dist/app/composables/preload')['preloadComponents']
|
||||||
|
const preloadPayload: typeof import('../../node_modules/nuxt/dist/app/composables/payload')['preloadPayload']
|
||||||
|
const preloadRouteComponents: typeof import('../../node_modules/nuxt/dist/app/composables/preload')['preloadRouteComponents']
|
||||||
|
const prerenderRoutes: typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['prerenderRoutes']
|
||||||
|
const provide: typeof import('vue')['provide']
|
||||||
|
const proxyRefs: typeof import('vue')['proxyRefs']
|
||||||
|
const reactive: typeof import('vue')['reactive']
|
||||||
|
const readonly: typeof import('vue')['readonly']
|
||||||
|
const ref: typeof import('vue')['ref']
|
||||||
|
const refreshCookie: typeof import('../../node_modules/nuxt/dist/app/composables/cookie')['refreshCookie']
|
||||||
|
const refreshNuxtData: typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['refreshNuxtData']
|
||||||
|
const reloadNuxtApp: typeof import('../../node_modules/nuxt/dist/app/composables/chunk')['reloadNuxtApp']
|
||||||
|
const requestIdleCallback: typeof import('../../node_modules/nuxt/dist/app/compat/idle-callback')['requestIdleCallback']
|
||||||
|
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||||
|
const setInterval: typeof import('../../node_modules/nuxt/dist/app/compat/interval')['setInterval']
|
||||||
|
const setPageLayout: typeof import('../../node_modules/nuxt/dist/app/composables/router')['setPageLayout']
|
||||||
|
const setResponseStatus: typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['setResponseStatus']
|
||||||
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||||
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||||
|
const shallowRef: typeof import('vue')['shallowRef']
|
||||||
|
const showError: typeof import('../../node_modules/nuxt/dist/app/composables/error')['showError']
|
||||||
|
const storeToRefs: typeof import('../../node_modules/@pinia/nuxt/dist/runtime/composables')['storeToRefs']
|
||||||
|
const toRaw: typeof import('vue')['toRaw']
|
||||||
|
const toRef: typeof import('vue')['toRef']
|
||||||
|
const toRefs: typeof import('vue')['toRefs']
|
||||||
|
const toValue: typeof import('vue')['toValue']
|
||||||
|
const triggerRef: typeof import('vue')['triggerRef']
|
||||||
|
const tryUseNuxtApp: typeof import('../../node_modules/nuxt/dist/app/nuxt')['tryUseNuxtApp']
|
||||||
|
const unref: typeof import('vue')['unref']
|
||||||
|
const updateAppConfig: typeof import('../../node_modules/nuxt/dist/app/config')['updateAppConfig']
|
||||||
|
const useAppConfig: typeof import('../../node_modules/nuxt/dist/app/config')['useAppConfig']
|
||||||
|
const useAsyncData: typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['useAsyncData']
|
||||||
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
|
const useCookie: typeof import('../../node_modules/nuxt/dist/app/composables/cookie')['useCookie']
|
||||||
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
|
const useCssVars: typeof import('vue')['useCssVars']
|
||||||
|
const useError: typeof import('../../node_modules/nuxt/dist/app/composables/error')['useError']
|
||||||
|
const useFetch: typeof import('../../node_modules/nuxt/dist/app/composables/fetch')['useFetch']
|
||||||
|
const useHead: typeof import('../../node_modules/nuxt/dist/app/composables/head')['useHead']
|
||||||
|
const useHeadSafe: typeof import('../../node_modules/nuxt/dist/app/composables/head')['useHeadSafe']
|
||||||
|
const useHydration: typeof import('../../node_modules/nuxt/dist/app/composables/hydrate')['useHydration']
|
||||||
|
const useId: typeof import('vue')['useId']
|
||||||
|
const useImage: typeof import('../../node_modules/@nuxt/image/dist/runtime/composables')['useImage']
|
||||||
|
const useLazyAsyncData: typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['useLazyAsyncData']
|
||||||
|
const useLazyFetch: typeof import('../../node_modules/nuxt/dist/app/composables/fetch')['useLazyFetch']
|
||||||
|
const useLink: typeof import('vue-router')['useLink']
|
||||||
|
const useLoadingIndicator: typeof import('../../node_modules/nuxt/dist/app/composables/loading-indicator')['useLoadingIndicator']
|
||||||
|
const useModel: typeof import('vue')['useModel']
|
||||||
|
const useNuxtApp: typeof import('../../node_modules/nuxt/dist/app/nuxt')['useNuxtApp']
|
||||||
|
const useNuxtData: typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['useNuxtData']
|
||||||
|
const useNuxtDevTools: typeof import('../../node_modules/@nuxt/devtools/dist/runtime/use-nuxt-devtools')['useNuxtDevTools']
|
||||||
|
const usePinia: typeof import('../../node_modules/@pinia/nuxt/dist/runtime/composables')['usePinia']
|
||||||
|
const usePreviewMode: typeof import('../../node_modules/nuxt/dist/app/composables/preview')['usePreviewMode']
|
||||||
|
const useRequestEvent: typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestEvent']
|
||||||
|
const useRequestFetch: typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestFetch']
|
||||||
|
const useRequestHeader: typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestHeader']
|
||||||
|
const useRequestHeaders: typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestHeaders']
|
||||||
|
const useRequestURL: typeof import('../../node_modules/nuxt/dist/app/composables/url')['useRequestURL']
|
||||||
|
const useResponseHeader: typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useResponseHeader']
|
||||||
|
const useRoute: typeof import('../../node_modules/nuxt/dist/app/composables/router')['useRoute']
|
||||||
|
const useRouteAnnouncer: typeof import('../../node_modules/nuxt/dist/app/composables/route-announcer')['useRouteAnnouncer']
|
||||||
|
const useRouter: typeof import('../../node_modules/nuxt/dist/app/composables/router')['useRouter']
|
||||||
|
const useRuntimeConfig: typeof import('../../node_modules/nuxt/dist/app/nuxt')['useRuntimeConfig']
|
||||||
|
const useRuntimeHook: typeof import('../../node_modules/nuxt/dist/app/composables/runtime-hook')['useRuntimeHook']
|
||||||
|
const useScript: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScript']
|
||||||
|
const useScriptClarity: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptClarity']
|
||||||
|
const useScriptCloudflareWebAnalytics: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptCloudflareWebAnalytics']
|
||||||
|
const useScriptCrisp: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptCrisp']
|
||||||
|
const useScriptEventPage: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptEventPage']
|
||||||
|
const useScriptFathomAnalytics: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptFathomAnalytics']
|
||||||
|
const useScriptGoogleAdsense: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptGoogleAdsense']
|
||||||
|
const useScriptGoogleAnalytics: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptGoogleAnalytics']
|
||||||
|
const useScriptGoogleMaps: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptGoogleMaps']
|
||||||
|
const useScriptGoogleTagManager: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptGoogleTagManager']
|
||||||
|
const useScriptHotjar: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptHotjar']
|
||||||
|
const useScriptIntercom: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptIntercom']
|
||||||
|
const useScriptLemonSqueezy: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptLemonSqueezy']
|
||||||
|
const useScriptMatomoAnalytics: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptMatomoAnalytics']
|
||||||
|
const useScriptMetaPixel: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptMetaPixel']
|
||||||
|
const useScriptNpm: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptNpm']
|
||||||
|
const useScriptPlausibleAnalytics: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptPlausibleAnalytics']
|
||||||
|
const useScriptSegment: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptSegment']
|
||||||
|
const useScriptSnapchatPixel: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptSnapchatPixel']
|
||||||
|
const useScriptStripe: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptStripe']
|
||||||
|
const useScriptTriggerConsent: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptTriggerConsent']
|
||||||
|
const useScriptTriggerElement: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptTriggerElement']
|
||||||
|
const useScriptUmamiAnalytics: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptUmamiAnalytics']
|
||||||
|
const useScriptVimeoPlayer: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptVimeoPlayer']
|
||||||
|
const useScriptXPixel: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptXPixel']
|
||||||
|
const useScriptYouTubePlayer: typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptYouTubePlayer']
|
||||||
|
const useSeoMeta: typeof import('../../node_modules/nuxt/dist/app/composables/head')['useSeoMeta']
|
||||||
|
const useServerHead: typeof import('../../node_modules/nuxt/dist/app/composables/head')['useServerHead']
|
||||||
|
const useServerHeadSafe: typeof import('../../node_modules/nuxt/dist/app/composables/head')['useServerHeadSafe']
|
||||||
|
const useServerSeoMeta: typeof import('../../node_modules/nuxt/dist/app/composables/head')['useServerSeoMeta']
|
||||||
|
const useShadowRoot: typeof import('vue')['useShadowRoot']
|
||||||
|
const useSlots: typeof import('vue')['useSlots']
|
||||||
|
const useState: typeof import('../../node_modules/nuxt/dist/app/composables/state')['useState']
|
||||||
|
const useStore: typeof import('../../stores/index')['useStore']
|
||||||
|
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
||||||
|
const useTransitionState: typeof import('vue')['useTransitionState']
|
||||||
|
const watch: typeof import('vue')['watch']
|
||||||
|
const watchEffect: typeof import('vue')['watchEffect']
|
||||||
|
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||||
|
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||||
|
const withCtx: typeof import('vue')['withCtx']
|
||||||
|
const withDirectives: typeof import('vue')['withDirectives']
|
||||||
|
const withKeys: typeof import('vue')['withKeys']
|
||||||
|
const withMemo: typeof import('vue')['withMemo']
|
||||||
|
const withModifiers: typeof import('vue')['withModifiers']
|
||||||
|
const withScopeId: typeof import('vue')['withScopeId']
|
||||||
|
}
|
||||||
|
// for type re-export
|
||||||
|
declare global {
|
||||||
|
// @ts-ignore
|
||||||
|
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||||
|
import('vue')
|
||||||
|
}
|
||||||
|
// for vue template auto import
|
||||||
|
import { UnwrapRef } from 'vue'
|
||||||
|
declare module 'vue' {
|
||||||
|
interface ComponentCustomProperties {
|
||||||
|
readonly abortNavigation: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/router')['abortNavigation']>
|
||||||
|
readonly acceptHMRUpdate: UnwrapRef<typeof import('../../node_modules/@pinia/nuxt/dist/runtime/composables')['acceptHMRUpdate']>
|
||||||
|
readonly addRouteMiddleware: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/router')['addRouteMiddleware']>
|
||||||
|
readonly callOnce: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/once')['callOnce']>
|
||||||
|
readonly cancelIdleCallback: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/compat/idle-callback')['cancelIdleCallback']>
|
||||||
|
readonly clearError: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/error')['clearError']>
|
||||||
|
readonly clearNuxtData: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['clearNuxtData']>
|
||||||
|
readonly clearNuxtState: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/state')['clearNuxtState']>
|
||||||
|
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||||
|
readonly createError: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/error')['createError']>
|
||||||
|
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||||
|
readonly defineAppConfig: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/nuxt')['defineAppConfig']>
|
||||||
|
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||||
|
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||||
|
readonly defineNuxtComponent: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/component')['defineNuxtComponent']>
|
||||||
|
readonly defineNuxtLink: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/components/nuxt-link')['defineNuxtLink']>
|
||||||
|
readonly defineNuxtPlugin: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/nuxt')['defineNuxtPlugin']>
|
||||||
|
readonly defineNuxtRouteMiddleware: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/router')['defineNuxtRouteMiddleware']>
|
||||||
|
readonly definePageMeta: UnwrapRef<typeof import('../../node_modules/nuxt/dist/pages/runtime/composables')['definePageMeta']>
|
||||||
|
readonly definePayloadPlugin: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/nuxt')['definePayloadPlugin']>
|
||||||
|
readonly definePayloadReducer: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/payload')['definePayloadReducer']>
|
||||||
|
readonly definePayloadReviver: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/payload')['definePayloadReviver']>
|
||||||
|
readonly defineStore: UnwrapRef<typeof import('../../node_modules/@pinia/nuxt/dist/runtime/composables')['defineStore']>
|
||||||
|
readonly effect: UnwrapRef<typeof import('vue')['effect']>
|
||||||
|
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||||
|
readonly getAppManifest: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/manifest')['getAppManifest']>
|
||||||
|
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||||
|
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||||
|
readonly getRouteRules: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/manifest')['getRouteRules']>
|
||||||
|
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||||
|
readonly hasInjectionContext: UnwrapRef<typeof import('vue')['hasInjectionContext']>
|
||||||
|
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||||
|
readonly injectHead: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/head')['injectHead']>
|
||||||
|
readonly isNuxtError: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/error')['isNuxtError']>
|
||||||
|
readonly isPrerendered: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/payload')['isPrerendered']>
|
||||||
|
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||||
|
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||||
|
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||||
|
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||||
|
readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']>
|
||||||
|
readonly isVue2: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/compat/vue-demi')['isVue2']>
|
||||||
|
readonly isVue3: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/compat/vue-demi')['isVue3']>
|
||||||
|
readonly loadPayload: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/payload')['loadPayload']>
|
||||||
|
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||||
|
readonly mergeModels: UnwrapRef<typeof import('vue')['mergeModels']>
|
||||||
|
readonly navigateTo: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/router')['navigateTo']>
|
||||||
|
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||||
|
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||||
|
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||||
|
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router')['onBeforeRouteLeave']>
|
||||||
|
readonly onBeforeRouteUpdate: UnwrapRef<typeof import('vue-router')['onBeforeRouteUpdate']>
|
||||||
|
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||||
|
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||||
|
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||||
|
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||||
|
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||||
|
readonly onNuxtReady: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ready')['onNuxtReady']>
|
||||||
|
readonly onPrehydrate: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['onPrehydrate']>
|
||||||
|
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||||
|
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||||
|
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||||
|
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||||
|
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||||
|
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||||
|
readonly piniaPluginPersistedstate: UnwrapRef<typeof import('../../node_modules/pinia-plugin-persistedstate/dist/nuxt/runtime/storages')['storages']>
|
||||||
|
readonly prefetchComponents: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/preload')['prefetchComponents']>
|
||||||
|
readonly preloadComponents: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/preload')['preloadComponents']>
|
||||||
|
readonly preloadPayload: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/payload')['preloadPayload']>
|
||||||
|
readonly preloadRouteComponents: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/preload')['preloadRouteComponents']>
|
||||||
|
readonly prerenderRoutes: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['prerenderRoutes']>
|
||||||
|
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||||
|
readonly proxyRefs: UnwrapRef<typeof import('vue')['proxyRefs']>
|
||||||
|
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||||
|
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||||
|
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||||
|
readonly refreshCookie: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/cookie')['refreshCookie']>
|
||||||
|
readonly refreshNuxtData: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['refreshNuxtData']>
|
||||||
|
readonly reloadNuxtApp: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/chunk')['reloadNuxtApp']>
|
||||||
|
readonly requestIdleCallback: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/compat/idle-callback')['requestIdleCallback']>
|
||||||
|
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||||
|
readonly setInterval: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/compat/interval')['setInterval']>
|
||||||
|
readonly setPageLayout: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/router')['setPageLayout']>
|
||||||
|
readonly setResponseStatus: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['setResponseStatus']>
|
||||||
|
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||||
|
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||||
|
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||||
|
readonly showError: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/error')['showError']>
|
||||||
|
readonly storeToRefs: UnwrapRef<typeof import('../../node_modules/@pinia/nuxt/dist/runtime/composables')['storeToRefs']>
|
||||||
|
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||||
|
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||||
|
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||||
|
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||||
|
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||||
|
readonly tryUseNuxtApp: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/nuxt')['tryUseNuxtApp']>
|
||||||
|
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||||
|
readonly updateAppConfig: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/config')['updateAppConfig']>
|
||||||
|
readonly useAppConfig: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/config')['useAppConfig']>
|
||||||
|
readonly useAsyncData: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['useAsyncData']>
|
||||||
|
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||||
|
readonly useCookie: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/cookie')['useCookie']>
|
||||||
|
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||||
|
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||||
|
readonly useError: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/error')['useError']>
|
||||||
|
readonly useFetch: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/fetch')['useFetch']>
|
||||||
|
readonly useHead: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/head')['useHead']>
|
||||||
|
readonly useHeadSafe: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/head')['useHeadSafe']>
|
||||||
|
readonly useHydration: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/hydrate')['useHydration']>
|
||||||
|
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
||||||
|
readonly useImage: UnwrapRef<typeof import('../../node_modules/@nuxt/image/dist/runtime/composables')['useImage']>
|
||||||
|
readonly useLazyAsyncData: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['useLazyAsyncData']>
|
||||||
|
readonly useLazyFetch: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/fetch')['useLazyFetch']>
|
||||||
|
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
|
||||||
|
readonly useLoadingIndicator: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/loading-indicator')['useLoadingIndicator']>
|
||||||
|
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
||||||
|
readonly useNuxtApp: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/nuxt')['useNuxtApp']>
|
||||||
|
readonly useNuxtData: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['useNuxtData']>
|
||||||
|
readonly useNuxtDevTools: UnwrapRef<typeof import('../../node_modules/@nuxt/devtools/dist/runtime/use-nuxt-devtools')['useNuxtDevTools']>
|
||||||
|
readonly usePinia: UnwrapRef<typeof import('../../node_modules/@pinia/nuxt/dist/runtime/composables')['usePinia']>
|
||||||
|
readonly usePreviewMode: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/preview')['usePreviewMode']>
|
||||||
|
readonly useRequestEvent: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestEvent']>
|
||||||
|
readonly useRequestFetch: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestFetch']>
|
||||||
|
readonly useRequestHeader: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestHeader']>
|
||||||
|
readonly useRequestHeaders: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestHeaders']>
|
||||||
|
readonly useRequestURL: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/url')['useRequestURL']>
|
||||||
|
readonly useResponseHeader: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useResponseHeader']>
|
||||||
|
readonly useRoute: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/router')['useRoute']>
|
||||||
|
readonly useRouteAnnouncer: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/route-announcer')['useRouteAnnouncer']>
|
||||||
|
readonly useRouter: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/router')['useRouter']>
|
||||||
|
readonly useRuntimeConfig: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/nuxt')['useRuntimeConfig']>
|
||||||
|
readonly useRuntimeHook: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/runtime-hook')['useRuntimeHook']>
|
||||||
|
readonly useScript: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScript']>
|
||||||
|
readonly useScriptClarity: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptClarity']>
|
||||||
|
readonly useScriptCloudflareWebAnalytics: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptCloudflareWebAnalytics']>
|
||||||
|
readonly useScriptCrisp: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptCrisp']>
|
||||||
|
readonly useScriptEventPage: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptEventPage']>
|
||||||
|
readonly useScriptFathomAnalytics: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptFathomAnalytics']>
|
||||||
|
readonly useScriptGoogleAdsense: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptGoogleAdsense']>
|
||||||
|
readonly useScriptGoogleAnalytics: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptGoogleAnalytics']>
|
||||||
|
readonly useScriptGoogleMaps: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptGoogleMaps']>
|
||||||
|
readonly useScriptGoogleTagManager: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptGoogleTagManager']>
|
||||||
|
readonly useScriptHotjar: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptHotjar']>
|
||||||
|
readonly useScriptIntercom: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptIntercom']>
|
||||||
|
readonly useScriptLemonSqueezy: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptLemonSqueezy']>
|
||||||
|
readonly useScriptMatomoAnalytics: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptMatomoAnalytics']>
|
||||||
|
readonly useScriptMetaPixel: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptMetaPixel']>
|
||||||
|
readonly useScriptNpm: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptNpm']>
|
||||||
|
readonly useScriptPlausibleAnalytics: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptPlausibleAnalytics']>
|
||||||
|
readonly useScriptSegment: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptSegment']>
|
||||||
|
readonly useScriptSnapchatPixel: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptSnapchatPixel']>
|
||||||
|
readonly useScriptStripe: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptStripe']>
|
||||||
|
readonly useScriptTriggerConsent: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptTriggerConsent']>
|
||||||
|
readonly useScriptTriggerElement: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptTriggerElement']>
|
||||||
|
readonly useScriptUmamiAnalytics: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptUmamiAnalytics']>
|
||||||
|
readonly useScriptVimeoPlayer: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptVimeoPlayer']>
|
||||||
|
readonly useScriptXPixel: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptXPixel']>
|
||||||
|
readonly useScriptYouTubePlayer: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/script-stubs')['useScriptYouTubePlayer']>
|
||||||
|
readonly useSeoMeta: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/head')['useSeoMeta']>
|
||||||
|
readonly useServerHead: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/head')['useServerHead']>
|
||||||
|
readonly useServerHeadSafe: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/head')['useServerHeadSafe']>
|
||||||
|
readonly useServerSeoMeta: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/head')['useServerSeoMeta']>
|
||||||
|
readonly useShadowRoot: UnwrapRef<typeof import('vue')['useShadowRoot']>
|
||||||
|
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||||
|
readonly useState: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/state')['useState']>
|
||||||
|
readonly useStore: UnwrapRef<typeof import('../../stores/index')['useStore']>
|
||||||
|
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
|
||||||
|
readonly useTransitionState: UnwrapRef<typeof import('vue')['useTransitionState']>
|
||||||
|
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||||
|
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||||
|
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||||
|
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||||
|
readonly withCtx: UnwrapRef<typeof import('vue')['withCtx']>
|
||||||
|
readonly withDirectives: UnwrapRef<typeof import('vue')['withDirectives']>
|
||||||
|
readonly withKeys: UnwrapRef<typeof import('vue')['withKeys']>
|
||||||
|
readonly withMemo: UnwrapRef<typeof import('vue')['withMemo']>
|
||||||
|
readonly withModifiers: UnwrapRef<typeof import('vue')['withModifiers']>
|
||||||
|
readonly withScopeId: UnwrapRef<typeof import('vue')['withScopeId']>
|
||||||
|
}
|
||||||
|
}
|
||||||
7
.nuxt/types/layouts.d.ts
vendored
Normal file
7
.nuxt/types/layouts.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { ComputedRef, MaybeRef } from 'vue'
|
||||||
|
export type LayoutKey = "default"
|
||||||
|
declare module 'nuxt/app' {
|
||||||
|
interface PageMeta {
|
||||||
|
layout?: MaybeRef<LayoutKey | false> | ComputedRef<LayoutKey | false>
|
||||||
|
}
|
||||||
|
}
|
||||||
7
.nuxt/types/middleware.d.ts
vendored
Normal file
7
.nuxt/types/middleware.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { NavigationGuard } from 'vue-router'
|
||||||
|
export type MiddlewareKey = never
|
||||||
|
declare module 'nuxt/app' {
|
||||||
|
interface PageMeta {
|
||||||
|
middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard>
|
||||||
|
}
|
||||||
|
}
|
||||||
14
.nuxt/types/nitro-config.d.ts
vendored
Normal file
14
.nuxt/types/nitro-config.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// Generated by nitro
|
||||||
|
|
||||||
|
// App Config
|
||||||
|
import type { Defu } from 'defu'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type UserAppConfig = Defu<{}, []>
|
||||||
|
|
||||||
|
declare module "nitropack/types" {
|
||||||
|
interface AppConfig extends UserAppConfig {}
|
||||||
|
|
||||||
|
}
|
||||||
|
export {}
|
||||||
141
.nuxt/types/nitro-imports.d.ts
vendored
Normal file
141
.nuxt/types/nitro-imports.d.ts
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
declare global {
|
||||||
|
const __buildAssetsURL: typeof import('../../node_modules/nuxt/dist/core/runtime/nitro/utils/paths')['buildAssetsURL']
|
||||||
|
const __publicAssetsURL: typeof import('../../node_modules/nuxt/dist/core/runtime/nitro/utils/paths')['publicAssetsURL']
|
||||||
|
const appendCorsHeaders: typeof import('../../node_modules/h3')['appendCorsHeaders']
|
||||||
|
const appendCorsPreflightHeaders: typeof import('../../node_modules/h3')['appendCorsPreflightHeaders']
|
||||||
|
const appendHeader: typeof import('../../node_modules/h3')['appendHeader']
|
||||||
|
const appendHeaders: typeof import('../../node_modules/h3')['appendHeaders']
|
||||||
|
const appendResponseHeader: typeof import('../../node_modules/h3')['appendResponseHeader']
|
||||||
|
const appendResponseHeaders: typeof import('../../node_modules/h3')['appendResponseHeaders']
|
||||||
|
const assertMethod: typeof import('../../node_modules/h3')['assertMethod']
|
||||||
|
const cachedEventHandler: typeof import('../../node_modules/nitropack/dist/runtime/internal/cache')['cachedEventHandler']
|
||||||
|
const cachedFunction: typeof import('../../node_modules/nitropack/dist/runtime/internal/cache')['cachedFunction']
|
||||||
|
const callNodeListener: typeof import('../../node_modules/h3')['callNodeListener']
|
||||||
|
const clearResponseHeaders: typeof import('../../node_modules/h3')['clearResponseHeaders']
|
||||||
|
const clearSession: typeof import('../../node_modules/h3')['clearSession']
|
||||||
|
const createApp: typeof import('../../node_modules/h3')['createApp']
|
||||||
|
const createAppEventHandler: typeof import('../../node_modules/h3')['createAppEventHandler']
|
||||||
|
const createError: typeof import('../../node_modules/h3')['createError']
|
||||||
|
const createEvent: typeof import('../../node_modules/h3')['createEvent']
|
||||||
|
const createEventStream: typeof import('../../node_modules/h3')['createEventStream']
|
||||||
|
const createRouter: typeof import('../../node_modules/h3')['createRouter']
|
||||||
|
const defaultContentType: typeof import('../../node_modules/h3')['defaultContentType']
|
||||||
|
const defineAppConfig: typeof import('../../node_modules/nuxt/dist/core/runtime/nitro/utils/config')['defineAppConfig']
|
||||||
|
const defineCachedEventHandler: typeof import('../../node_modules/nitropack/dist/runtime/internal/cache')['defineCachedEventHandler']
|
||||||
|
const defineCachedFunction: typeof import('../../node_modules/nitropack/dist/runtime/internal/cache')['defineCachedFunction']
|
||||||
|
const defineEventHandler: typeof import('../../node_modules/h3')['defineEventHandler']
|
||||||
|
const defineLazyEventHandler: typeof import('../../node_modules/h3')['defineLazyEventHandler']
|
||||||
|
const defineNitroErrorHandler: typeof import('../../node_modules/nitropack/dist/runtime/internal/error/utils')['defineNitroErrorHandler']
|
||||||
|
const defineNitroPlugin: typeof import('../../node_modules/nitropack/dist/runtime/internal/plugin')['defineNitroPlugin']
|
||||||
|
const defineNodeListener: typeof import('../../node_modules/h3')['defineNodeListener']
|
||||||
|
const defineNodeMiddleware: typeof import('../../node_modules/h3')['defineNodeMiddleware']
|
||||||
|
const defineRenderHandler: typeof import('../../node_modules/nitropack/dist/runtime/internal/renderer')['defineRenderHandler']
|
||||||
|
const defineRequestMiddleware: typeof import('../../node_modules/h3')['defineRequestMiddleware']
|
||||||
|
const defineResponseMiddleware: typeof import('../../node_modules/h3')['defineResponseMiddleware']
|
||||||
|
const defineRouteMeta: typeof import('../../node_modules/nitropack/dist/runtime/internal/meta')['defineRouteMeta']
|
||||||
|
const defineTask: typeof import('../../node_modules/nitropack/dist/runtime/internal/task')['defineTask']
|
||||||
|
const defineWebSocket: typeof import('../../node_modules/h3')['defineWebSocket']
|
||||||
|
const defineWebSocketHandler: typeof import('../../node_modules/h3')['defineWebSocketHandler']
|
||||||
|
const deleteCookie: typeof import('../../node_modules/h3')['deleteCookie']
|
||||||
|
const dynamicEventHandler: typeof import('../../node_modules/h3')['dynamicEventHandler']
|
||||||
|
const eventHandler: typeof import('../../node_modules/h3')['eventHandler']
|
||||||
|
const fetchWithEvent: typeof import('../../node_modules/h3')['fetchWithEvent']
|
||||||
|
const fromNodeMiddleware: typeof import('../../node_modules/h3')['fromNodeMiddleware']
|
||||||
|
const fromPlainHandler: typeof import('../../node_modules/h3')['fromPlainHandler']
|
||||||
|
const fromWebHandler: typeof import('../../node_modules/h3')['fromWebHandler']
|
||||||
|
const getCookie: typeof import('../../node_modules/h3')['getCookie']
|
||||||
|
const getHeader: typeof import('../../node_modules/h3')['getHeader']
|
||||||
|
const getHeaders: typeof import('../../node_modules/h3')['getHeaders']
|
||||||
|
const getMethod: typeof import('../../node_modules/h3')['getMethod']
|
||||||
|
const getProxyRequestHeaders: typeof import('../../node_modules/h3')['getProxyRequestHeaders']
|
||||||
|
const getQuery: typeof import('../../node_modules/h3')['getQuery']
|
||||||
|
const getRequestFingerprint: typeof import('../../node_modules/h3')['getRequestFingerprint']
|
||||||
|
const getRequestHeader: typeof import('../../node_modules/h3')['getRequestHeader']
|
||||||
|
const getRequestHeaders: typeof import('../../node_modules/h3')['getRequestHeaders']
|
||||||
|
const getRequestHost: typeof import('../../node_modules/h3')['getRequestHost']
|
||||||
|
const getRequestIP: typeof import('../../node_modules/h3')['getRequestIP']
|
||||||
|
const getRequestPath: typeof import('../../node_modules/h3')['getRequestPath']
|
||||||
|
const getRequestProtocol: typeof import('../../node_modules/h3')['getRequestProtocol']
|
||||||
|
const getRequestURL: typeof import('../../node_modules/h3')['getRequestURL']
|
||||||
|
const getRequestWebStream: typeof import('../../node_modules/h3')['getRequestWebStream']
|
||||||
|
const getResponseHeader: typeof import('../../node_modules/h3')['getResponseHeader']
|
||||||
|
const getResponseHeaders: typeof import('../../node_modules/h3')['getResponseHeaders']
|
||||||
|
const getResponseStatus: typeof import('../../node_modules/h3')['getResponseStatus']
|
||||||
|
const getResponseStatusText: typeof import('../../node_modules/h3')['getResponseStatusText']
|
||||||
|
const getRouteRules: typeof import('../../node_modules/nitropack/dist/runtime/internal/route-rules')['getRouteRules']
|
||||||
|
const getRouterParam: typeof import('../../node_modules/h3')['getRouterParam']
|
||||||
|
const getRouterParams: typeof import('../../node_modules/h3')['getRouterParams']
|
||||||
|
const getSession: typeof import('../../node_modules/h3')['getSession']
|
||||||
|
const getValidatedQuery: typeof import('../../node_modules/h3')['getValidatedQuery']
|
||||||
|
const getValidatedRouterParams: typeof import('../../node_modules/h3')['getValidatedRouterParams']
|
||||||
|
const handleCacheHeaders: typeof import('../../node_modules/h3')['handleCacheHeaders']
|
||||||
|
const handleCors: typeof import('../../node_modules/h3')['handleCors']
|
||||||
|
const isCorsOriginAllowed: typeof import('../../node_modules/h3')['isCorsOriginAllowed']
|
||||||
|
const isError: typeof import('../../node_modules/h3')['isError']
|
||||||
|
const isEvent: typeof import('../../node_modules/h3')['isEvent']
|
||||||
|
const isEventHandler: typeof import('../../node_modules/h3')['isEventHandler']
|
||||||
|
const isMethod: typeof import('../../node_modules/h3')['isMethod']
|
||||||
|
const isPreflightRequest: typeof import('../../node_modules/h3')['isPreflightRequest']
|
||||||
|
const isStream: typeof import('../../node_modules/h3')['isStream']
|
||||||
|
const isWebResponse: typeof import('../../node_modules/h3')['isWebResponse']
|
||||||
|
const lazyEventHandler: typeof import('../../node_modules/h3')['lazyEventHandler']
|
||||||
|
const nitroPlugin: typeof import('../../node_modules/nitropack/dist/runtime/internal/plugin')['nitroPlugin']
|
||||||
|
const parseCookies: typeof import('../../node_modules/h3')['parseCookies']
|
||||||
|
const promisifyNodeListener: typeof import('../../node_modules/h3')['promisifyNodeListener']
|
||||||
|
const proxyRequest: typeof import('../../node_modules/h3')['proxyRequest']
|
||||||
|
const readBody: typeof import('../../node_modules/h3')['readBody']
|
||||||
|
const readFormData: typeof import('../../node_modules/h3')['readFormData']
|
||||||
|
const readMultipartFormData: typeof import('../../node_modules/h3')['readMultipartFormData']
|
||||||
|
const readRawBody: typeof import('../../node_modules/h3')['readRawBody']
|
||||||
|
const readValidatedBody: typeof import('../../node_modules/h3')['readValidatedBody']
|
||||||
|
const removeResponseHeader: typeof import('../../node_modules/h3')['removeResponseHeader']
|
||||||
|
const runTask: typeof import('../../node_modules/nitropack/dist/runtime/internal/task')['runTask']
|
||||||
|
const sanitizeStatusCode: typeof import('../../node_modules/h3')['sanitizeStatusCode']
|
||||||
|
const sanitizeStatusMessage: typeof import('../../node_modules/h3')['sanitizeStatusMessage']
|
||||||
|
const sealSession: typeof import('../../node_modules/h3')['sealSession']
|
||||||
|
const send: typeof import('../../node_modules/h3')['send']
|
||||||
|
const sendError: typeof import('../../node_modules/h3')['sendError']
|
||||||
|
const sendIterable: typeof import('../../node_modules/h3')['sendIterable']
|
||||||
|
const sendNoContent: typeof import('../../node_modules/h3')['sendNoContent']
|
||||||
|
const sendProxy: typeof import('../../node_modules/h3')['sendProxy']
|
||||||
|
const sendRedirect: typeof import('../../node_modules/h3')['sendRedirect']
|
||||||
|
const sendStream: typeof import('../../node_modules/h3')['sendStream']
|
||||||
|
const sendWebResponse: typeof import('../../node_modules/h3')['sendWebResponse']
|
||||||
|
const serveStatic: typeof import('../../node_modules/h3')['serveStatic']
|
||||||
|
const setCookie: typeof import('../../node_modules/h3')['setCookie']
|
||||||
|
const setHeader: typeof import('../../node_modules/h3')['setHeader']
|
||||||
|
const setHeaders: typeof import('../../node_modules/h3')['setHeaders']
|
||||||
|
const setResponseHeader: typeof import('../../node_modules/h3')['setResponseHeader']
|
||||||
|
const setResponseHeaders: typeof import('../../node_modules/h3')['setResponseHeaders']
|
||||||
|
const setResponseStatus: typeof import('../../node_modules/h3')['setResponseStatus']
|
||||||
|
const splitCookiesString: typeof import('../../node_modules/h3')['splitCookiesString']
|
||||||
|
const toEventHandler: typeof import('../../node_modules/h3')['toEventHandler']
|
||||||
|
const toNodeListener: typeof import('../../node_modules/h3')['toNodeListener']
|
||||||
|
const toPlainHandler: typeof import('../../node_modules/h3')['toPlainHandler']
|
||||||
|
const toWebHandler: typeof import('../../node_modules/h3')['toWebHandler']
|
||||||
|
const toWebRequest: typeof import('../../node_modules/h3')['toWebRequest']
|
||||||
|
const unsealSession: typeof import('../../node_modules/h3')['unsealSession']
|
||||||
|
const updateSession: typeof import('../../node_modules/h3')['updateSession']
|
||||||
|
const useAppConfig: typeof import('../../node_modules/nitropack/dist/runtime/internal/config')['useAppConfig']
|
||||||
|
const useBase: typeof import('../../node_modules/h3')['useBase']
|
||||||
|
const useEvent: typeof import('../../node_modules/nitropack/dist/runtime/internal/context')['useEvent']
|
||||||
|
const useNitroApp: typeof import('../../node_modules/nitropack/dist/runtime/internal/app')['useNitroApp']
|
||||||
|
const useRuntimeConfig: typeof import('../../node_modules/nitropack/dist/runtime/internal/config')['useRuntimeConfig']
|
||||||
|
const useSession: typeof import('../../node_modules/h3')['useSession']
|
||||||
|
const useStorage: typeof import('../../node_modules/nitropack/dist/runtime/internal/storage')['useStorage']
|
||||||
|
const writeEarlyHints: typeof import('../../node_modules/h3')['writeEarlyHints']
|
||||||
|
}
|
||||||
|
export { useNitroApp } from 'nitropack/runtime/internal/app';
|
||||||
|
export { useRuntimeConfig, useAppConfig } from 'nitropack/runtime/internal/config';
|
||||||
|
export { defineNitroPlugin, nitroPlugin } from 'nitropack/runtime/internal/plugin';
|
||||||
|
export { defineCachedFunction, defineCachedEventHandler, cachedFunction, cachedEventHandler } from 'nitropack/runtime/internal/cache';
|
||||||
|
export { useStorage } from 'nitropack/runtime/internal/storage';
|
||||||
|
export { defineRenderHandler } from 'nitropack/runtime/internal/renderer';
|
||||||
|
export { defineRouteMeta } from 'nitropack/runtime/internal/meta';
|
||||||
|
export { getRouteRules } from 'nitropack/runtime/internal/route-rules';
|
||||||
|
export { useEvent } from 'nitropack/runtime/internal/context';
|
||||||
|
export { defineTask, runTask } from 'nitropack/runtime/internal/task';
|
||||||
|
export { defineNitroErrorHandler } from 'nitropack/runtime/internal/error/utils';
|
||||||
|
export { appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearResponseHeaders, clearSession, createApp, createAppEventHandler, createError, createEvent, createEventStream, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, defineRequestMiddleware, defineResponseMiddleware, defineWebSocket, defineWebSocketHandler, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, fromPlainHandler, fromWebHandler, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestFingerprint, getRequestHeader, getRequestHeaders, getRequestHost, getRequestIP, getRequestPath, getRequestProtocol, getRequestURL, getRequestWebStream, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, getValidatedQuery, getValidatedRouterParams, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, isWebResponse, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readFormData, readMultipartFormData, readRawBody, readValidatedBody, removeResponseHeader, sanitizeStatusCode, sanitizeStatusMessage, sealSession, send, sendError, sendIterable, sendNoContent, sendProxy, sendRedirect, sendStream, sendWebResponse, serveStatic, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, splitCookiesString, toEventHandler, toNodeListener, toPlainHandler, toWebHandler, toWebRequest, unsealSession, updateSession, useBase, useSession, writeEarlyHints } from 'h3';
|
||||||
|
export { buildAssetsURL as __buildAssetsURL, publicAssetsURL as __publicAssetsURL } from '/home/loitx/dev/utopia/system/node_modules/nuxt/dist/core/runtime/nitro/utils/paths';
|
||||||
|
export { defineAppConfig } from '/home/loitx/dev/utopia/system/node_modules/nuxt/dist/core/runtime/nitro/utils/config';
|
||||||
6
.nuxt/types/nitro-middleware.d.ts
vendored
Normal file
6
.nuxt/types/nitro-middleware.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export type MiddlewareKey = never
|
||||||
|
declare module 'nitropack' {
|
||||||
|
interface NitroRouteConfig {
|
||||||
|
appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record<MiddlewareKey, boolean>
|
||||||
|
}
|
||||||
|
}
|
||||||
34
.nuxt/types/nitro-nuxt.d.ts
vendored
Normal file
34
.nuxt/types/nitro-nuxt.d.ts
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
/// <reference path="nitro-middleware.d.ts" />
|
||||||
|
/// <reference path="./schema.d.ts" />
|
||||||
|
|
||||||
|
import type { RuntimeConfig } from 'nuxt/schema'
|
||||||
|
import type { H3Event } from 'h3'
|
||||||
|
import type { LogObject } from 'consola'
|
||||||
|
import type { NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext } from 'nuxt/app'
|
||||||
|
|
||||||
|
declare module 'nitropack' {
|
||||||
|
interface NitroRuntimeConfigApp {
|
||||||
|
buildAssetsDir: string
|
||||||
|
cdnURL: string
|
||||||
|
}
|
||||||
|
interface NitroRuntimeConfig extends RuntimeConfig {}
|
||||||
|
interface NitroRouteConfig {
|
||||||
|
ssr?: boolean
|
||||||
|
noScripts?: boolean
|
||||||
|
/** @deprecated Use `noScripts` instead */
|
||||||
|
experimentalNoScripts?: boolean
|
||||||
|
}
|
||||||
|
interface NitroRouteRules {
|
||||||
|
ssr?: boolean
|
||||||
|
noScripts?: boolean
|
||||||
|
/** @deprecated Use `noScripts` instead */
|
||||||
|
experimentalNoScripts?: boolean
|
||||||
|
appMiddleware?: Record<string, boolean>
|
||||||
|
}
|
||||||
|
interface NitroRuntimeHooks {
|
||||||
|
'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise<void>
|
||||||
|
'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise<void>
|
||||||
|
'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise<void>
|
||||||
|
}
|
||||||
|
}
|
||||||
17
.nuxt/types/nitro-routes.d.ts
vendored
Normal file
17
.nuxt/types/nitro-routes.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Generated by nitro
|
||||||
|
import type { Serialize, Simplify } from "nitropack/types";
|
||||||
|
declare module "nitropack/types" {
|
||||||
|
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
|
||||||
|
interface InternalApi {
|
||||||
|
'/__nuxt_error': {
|
||||||
|
'default': Simplify<Serialize<Awaited<ReturnType<typeof import('../../node_modules/nuxt/dist/core/runtime/nitro/handlers/renderer').default>>>>
|
||||||
|
}
|
||||||
|
'/__nuxt_island/**': {
|
||||||
|
'default': Simplify<Serialize<Awaited<ReturnType<typeof import('../../server/#internal/nuxt/island-renderer').default>>>>
|
||||||
|
}
|
||||||
|
'/_ipx/**': {
|
||||||
|
'default': Simplify<Serialize<Awaited<ReturnType<typeof import('../../node_modules/@nuxt/image/dist/runtime/ipx').default>>>>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {}
|
||||||
3
.nuxt/types/nitro.d.ts
vendored
Normal file
3
.nuxt/types/nitro.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/// <reference path="./nitro-routes.d.ts" />
|
||||||
|
/// <reference path="./nitro-config.d.ts" />
|
||||||
|
/// <reference path="./nitro-imports.d.ts" />
|
||||||
44
.nuxt/types/plugins.d.ts
vendored
Normal file
44
.nuxt/types/plugins.d.ts
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Generated by Nuxt'
|
||||||
|
import type { Plugin } from '#app'
|
||||||
|
|
||||||
|
type Decorate<T extends Record<string, any>> = { [K in keyof T as K extends string ? `$${K}` : never]: T[K] }
|
||||||
|
|
||||||
|
type InjectionType<A extends Plugin> = A extends {default: Plugin<infer T>} ? Decorate<T> : unknown
|
||||||
|
|
||||||
|
type NuxtAppInjections =
|
||||||
|
InjectionType<typeof import("../../node_modules/@pinia/nuxt/dist/runtime/payload-plugin.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/revive-payload.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/head/runtime/plugins/unhead.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/pages/runtime/plugins/router.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/browser-devtools-timing.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/navigation-repaint.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/check-outdated-build.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/revive-payload.server.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/chunk-reload.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/@pinia/nuxt/dist/runtime/plugin.vue3.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/pages/runtime/plugins/prefetch.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/pages/runtime/plugins/check-if-page-unused.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/@nuxt/devtools/dist/runtime/plugins/devtools.server.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/@nuxt/devtools/dist/runtime/plugins/devtools.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/pinia-plugin-persistedstate/dist/nuxt/runtime/plugin.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/dev-server-logs.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/check-if-layout-used.js")> &
|
||||||
|
InjectionType<typeof import("../../plugins/00-datatable.js")> &
|
||||||
|
InjectionType<typeof import("../../plugins/01-common.js")> &
|
||||||
|
InjectionType<typeof import("../../plugins/02-connection.js")> &
|
||||||
|
InjectionType<typeof import("../../plugins/03-api-loader.js")> &
|
||||||
|
InjectionType<typeof import("../../plugins/04-components.js")>
|
||||||
|
|
||||||
|
declare module '#app' {
|
||||||
|
interface NuxtApp extends NuxtAppInjections { }
|
||||||
|
|
||||||
|
interface NuxtAppLiterals {
|
||||||
|
pluginName: 'vue-devtools-client' | 'nuxt:revive-payload:client' | 'nuxt:head' | 'nuxt:router' | 'nuxt:browser-devtools-timing' | 'nuxt:revive-payload:server' | 'nuxt:chunk-reload' | 'pinia' | 'nuxt:global-components' | 'nuxt:prefetch' | 'nuxt:checkIfPageUnused' | 'pinia-plugin-persistedstate' | 'nuxt:checkIfLayoutUsed'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
interface ComponentCustomProperties extends NuxtAppInjections { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export { }
|
||||||
128
.nuxt/types/schema.d.ts
vendored
Normal file
128
.nuxt/types/schema.d.ts
vendored
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import { NuxtModule, RuntimeConfig } from '@nuxt/schema'
|
||||||
|
declare module '@nuxt/schema' {
|
||||||
|
interface NuxtOptions {
|
||||||
|
/**
|
||||||
|
* Configuration for `@pinia/nuxt`
|
||||||
|
*/
|
||||||
|
["pinia"]: typeof import("@pinia/nuxt").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/image`
|
||||||
|
*/
|
||||||
|
["image"]: typeof import("@nuxt/image").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `pinia-plugin-persistedstate/nuxt`
|
||||||
|
*/
|
||||||
|
["piniaPluginPersistedstate"]: typeof import("pinia-plugin-persistedstate/nuxt").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/devtools`
|
||||||
|
*/
|
||||||
|
["devtools"]: typeof import("@nuxt/devtools").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/telemetry`
|
||||||
|
*/
|
||||||
|
["telemetry"]: typeof import("@nuxt/telemetry").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
}
|
||||||
|
interface NuxtConfig {
|
||||||
|
/**
|
||||||
|
* Configuration for `@pinia/nuxt`
|
||||||
|
*/
|
||||||
|
["pinia"]?: typeof import("@pinia/nuxt").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/image`
|
||||||
|
*/
|
||||||
|
["image"]?: typeof import("@nuxt/image").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `pinia-plugin-persistedstate/nuxt`
|
||||||
|
*/
|
||||||
|
["piniaPluginPersistedstate"]?: typeof import("pinia-plugin-persistedstate/nuxt").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/devtools`
|
||||||
|
*/
|
||||||
|
["devtools"]?: typeof import("@nuxt/devtools").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/telemetry`
|
||||||
|
*/
|
||||||
|
["telemetry"]?: typeof import("@nuxt/telemetry").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
modules?: (undefined | null | false | NuxtModule<any> | string | [NuxtModule | string, Record<string, any>] | ["@pinia/nuxt", Exclude<NuxtConfig["pinia"], boolean>] | ["@nuxt/image", Exclude<NuxtConfig["image"], boolean>] | ["pinia-plugin-persistedstate/nuxt", Exclude<NuxtConfig["piniaPluginPersistedstate"], boolean>] | ["@nuxt/devtools", Exclude<NuxtConfig["devtools"], boolean>] | ["@nuxt/telemetry", Exclude<NuxtConfig["telemetry"], boolean>])[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declare module 'nuxt/schema' {
|
||||||
|
interface NuxtOptions {
|
||||||
|
/**
|
||||||
|
* Configuration for `@pinia/nuxt`
|
||||||
|
* @see https://www.npmjs.com/package/@pinia/nuxt
|
||||||
|
*/
|
||||||
|
["pinia"]: typeof import("@pinia/nuxt").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/image`
|
||||||
|
* @see https://www.npmjs.com/package/@nuxt/image
|
||||||
|
*/
|
||||||
|
["image"]: typeof import("@nuxt/image").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `pinia-plugin-persistedstate/nuxt`
|
||||||
|
* @see https://www.npmjs.com/package/pinia-plugin-persistedstate/nuxt
|
||||||
|
*/
|
||||||
|
["piniaPluginPersistedstate"]: typeof import("pinia-plugin-persistedstate/nuxt").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/devtools`
|
||||||
|
* @see https://www.npmjs.com/package/@nuxt/devtools
|
||||||
|
*/
|
||||||
|
["devtools"]: typeof import("@nuxt/devtools").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/telemetry`
|
||||||
|
* @see https://www.npmjs.com/package/@nuxt/telemetry
|
||||||
|
*/
|
||||||
|
["telemetry"]: typeof import("@nuxt/telemetry").default extends NuxtModule<infer O> ? O : Record<string, any>
|
||||||
|
}
|
||||||
|
interface NuxtConfig {
|
||||||
|
/**
|
||||||
|
* Configuration for `@pinia/nuxt`
|
||||||
|
* @see https://www.npmjs.com/package/@pinia/nuxt
|
||||||
|
*/
|
||||||
|
["pinia"]?: typeof import("@pinia/nuxt").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/image`
|
||||||
|
* @see https://www.npmjs.com/package/@nuxt/image
|
||||||
|
*/
|
||||||
|
["image"]?: typeof import("@nuxt/image").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `pinia-plugin-persistedstate/nuxt`
|
||||||
|
* @see https://www.npmjs.com/package/pinia-plugin-persistedstate/nuxt
|
||||||
|
*/
|
||||||
|
["piniaPluginPersistedstate"]?: typeof import("pinia-plugin-persistedstate/nuxt").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/devtools`
|
||||||
|
* @see https://www.npmjs.com/package/@nuxt/devtools
|
||||||
|
*/
|
||||||
|
["devtools"]?: typeof import("@nuxt/devtools").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
/**
|
||||||
|
* Configuration for `@nuxt/telemetry`
|
||||||
|
* @see https://www.npmjs.com/package/@nuxt/telemetry
|
||||||
|
*/
|
||||||
|
["telemetry"]?: typeof import("@nuxt/telemetry").default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
||||||
|
modules?: (undefined | null | false | NuxtModule<any> | string | [NuxtModule | string, Record<string, any>] | ["@pinia/nuxt", Exclude<NuxtConfig["pinia"], boolean>] | ["@nuxt/image", Exclude<NuxtConfig["image"], boolean>] | ["pinia-plugin-persistedstate/nuxt", Exclude<NuxtConfig["piniaPluginPersistedstate"], boolean>] | ["@nuxt/devtools", Exclude<NuxtConfig["devtools"], boolean>] | ["@nuxt/telemetry", Exclude<NuxtConfig["telemetry"], boolean>])[],
|
||||||
|
}
|
||||||
|
interface RuntimeConfig {
|
||||||
|
app: {
|
||||||
|
buildId: string,
|
||||||
|
|
||||||
|
baseURL: string,
|
||||||
|
|
||||||
|
buildAssetsDir: string,
|
||||||
|
|
||||||
|
cdnURL: string,
|
||||||
|
},
|
||||||
|
|
||||||
|
nitro: {
|
||||||
|
envPrefix: string,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
interface PublicRuntimeConfig {
|
||||||
|
piniaPluginPersistedstate: any,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declare module 'vue' {
|
||||||
|
interface ComponentCustomProperties {
|
||||||
|
$config: RuntimeConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
0
.nuxt/types/vue-shim.d.ts
vendored
Normal file
0
.nuxt/types/vue-shim.d.ts
vendored
Normal file
7
Dockerfile
Executable file
7
Dockerfile
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
FROM node:18-alpine
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
RUN rm -rf node_modules
|
||||||
|
RUN npm install --legacy-peer-deps
|
||||||
|
RUN npm run build
|
||||||
|
RUN npm install pm2 -g
|
||||||
75
README.md
Normal file
75
README.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Nuxt Minimal Starter
|
||||||
|
|
||||||
|
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Make sure to install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Server
|
||||||
|
|
||||||
|
Start the development server on `http://localhost:3000`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn dev
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production
|
||||||
|
|
||||||
|
Build the application for production:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Locally preview production build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm
|
||||||
|
npm run preview
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm preview
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn preview
|
||||||
|
|
||||||
|
# bun
|
||||||
|
bun run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
||||||
5
app.vue
Normal file
5
app.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<NuxtLayout>
|
||||||
|
<NuxtPage />
|
||||||
|
</NuxtLayout>
|
||||||
|
</template>
|
||||||
1
apps
Submodule
1
apps
Submodule
Submodule apps added at a67f7482df
335
assets/styles/main.scss
Executable file
335
assets/styles/main.scss
Executable file
@@ -0,0 +1,335 @@
|
|||||||
|
|
||||||
|
@use "bulma/sass/utilities/initial-variables.scss" as *;
|
||||||
|
@use "bulma/sass/utilities/mixins.scss" as *;
|
||||||
|
|
||||||
|
$color:(
|
||||||
|
primary: #3392ec, //#008000,
|
||||||
|
findata: #ff8829,
|
||||||
|
danger: #f14668,
|
||||||
|
dark: #686868,
|
||||||
|
twitter: #3392ec
|
||||||
|
);
|
||||||
|
$size: (
|
||||||
|
one: 10px,
|
||||||
|
two: 17px,
|
||||||
|
three: 30px,
|
||||||
|
four: 40px,
|
||||||
|
five: 50px,
|
||||||
|
six: 60px
|
||||||
|
);
|
||||||
|
|
||||||
|
@mixin cbox($width, $height, $font, $background) {
|
||||||
|
display:flex;
|
||||||
|
width: $width;
|
||||||
|
height: $height;
|
||||||
|
border: 1.5px solid #ff8829;
|
||||||
|
font-size: $font;
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
-moz-border-radius: 50%;
|
||||||
|
-ms-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #ff8829;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: $background;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
@each $name, $hex in $color {
|
||||||
|
@each $n, $v in $size {
|
||||||
|
.cbox-#{$name}-#{$n}{
|
||||||
|
@include cbox($v*2, $v*2, $v*1.1, white);
|
||||||
|
border-color: $hex ;
|
||||||
|
color: $hex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@each $name, $hex in $color {
|
||||||
|
@each $n, $v in $size {
|
||||||
|
.cbox-fill-#{$name}-#{$n}{
|
||||||
|
@include cbox($v*2, $v*2, $v, $hex);
|
||||||
|
border-color: $hex ;
|
||||||
|
color: findColorInvert($hex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@for $i from 10 through 40 {
|
||||||
|
.fs-#{$i} {
|
||||||
|
font-size: $i + px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@for $i from 10 through 40 {
|
||||||
|
.fsb-#{$i} {
|
||||||
|
font-size: $i + px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullheight {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textsize {
|
||||||
|
@include mobile {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-logo{
|
||||||
|
background: url('/logo.svg') no-repeat center center;
|
||||||
|
background-size: 140px;
|
||||||
|
width: 170px
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-bottom {
|
||||||
|
border-bottom: 1px solid hsl(0, 0%, 90%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-height {
|
||||||
|
width: 100%;
|
||||||
|
height: 80vh;
|
||||||
|
@include mobile {
|
||||||
|
height: 110vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-mt20 {
|
||||||
|
@include mobile {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-px10 {
|
||||||
|
@include mobile {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-pt10 {
|
||||||
|
@include mobile {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-pt80 {
|
||||||
|
padding-top: 120px;
|
||||||
|
@include mobile {
|
||||||
|
padding-top: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullhd-pt30 {
|
||||||
|
padding-top: 30px;
|
||||||
|
@include until($fullhd) {
|
||||||
|
padding-top: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-width {
|
||||||
|
width: 120px !important;
|
||||||
|
@include mobile {
|
||||||
|
width: 112px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hideon-mobile {
|
||||||
|
@include mobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.maintext {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 40px;
|
||||||
|
line-height:3rem;
|
||||||
|
font-weight: 600;
|
||||||
|
@include mobile {
|
||||||
|
font-size: 34px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtext {
|
||||||
|
margin-top: 30px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
@include mobile {
|
||||||
|
line-height: 1.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 10 through 50 {
|
||||||
|
.fs-#{$i} {
|
||||||
|
font-size: $i + px;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@for $i from 10 through 50 {
|
||||||
|
.fsb-#{$i} {
|
||||||
|
font-size: $i + px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dotslide {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activetab {
|
||||||
|
border: 2px solid #3392ec; //#008000; // #0AB412
|
||||||
|
border-radius: 8px;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
color: white;
|
||||||
|
background-color: #3392ec; //#008000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blockdiv {
|
||||||
|
max-width: 1900px !important;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
height: 100vh;
|
||||||
|
//margin-top: 40px !important;
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-top: 60px;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
|
||||||
|
@include until($desktop) {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
padding-top: 65px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mobile {
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right:16px;
|
||||||
|
padding-top: 65px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns .column {
|
||||||
|
@include mobile {
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right:0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-desktop {
|
||||||
|
@media screen and (min-width: $desktop) {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-text {
|
||||||
|
padding-left: 15%;
|
||||||
|
padding-right: 5%;
|
||||||
|
@include until($desktop) {
|
||||||
|
padding-left: 0%;
|
||||||
|
padding-right: 0%;
|
||||||
|
}
|
||||||
|
@include until($fullhd) {
|
||||||
|
padding-left: 0%;
|
||||||
|
padding-right: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-image{
|
||||||
|
padding-left: 5%;
|
||||||
|
padding-right: 15%;
|
||||||
|
@include until($fullhd) {
|
||||||
|
padding-left: 0%;
|
||||||
|
padding-right: 0%;
|
||||||
|
}
|
||||||
|
@include until($desktop) {
|
||||||
|
padding-left: 15%;
|
||||||
|
padding-right: 15%;
|
||||||
|
}
|
||||||
|
@include mobile {
|
||||||
|
padding-left: 0%;
|
||||||
|
padding-right: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgcontainer {
|
||||||
|
position: relative;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
.centered {
|
||||||
|
position: absolute;
|
||||||
|
top: 80%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
color:black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip .tooltiptext {
|
||||||
|
visibility: hidden;
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 0px;
|
||||||
|
z-index: 999;
|
||||||
|
bottom: 110%;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
padding: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.to-left {
|
||||||
|
right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin tooltipshow() {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
min-width: 300px;
|
||||||
|
z-index: 999;
|
||||||
|
background-color: #F7F18A;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover .tooltiptext {
|
||||||
|
@include tooltipshow()
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover .tooltiptext .to-left {
|
||||||
|
@include tooltipshow()
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dot($background) {
|
||||||
|
height: 22px;
|
||||||
|
width: 22px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: $background;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $name, $hex in $color {
|
||||||
|
.dot-#{$name} {
|
||||||
|
@include dot($hex);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
build.sh
Normal file
6
build.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
python3 envprod.py
|
||||||
|
PROJECT="utopia"
|
||||||
|
IMAGE="system"
|
||||||
|
|
||||||
|
docker build -t docker.bigdatatech.vn/$PROJECT/$IMAGE:latest .
|
||||||
|
docker push docker.bigdatatech.vn/$PROJECT/$IMAGE:latest
|
||||||
6
builddev.sh
Normal file
6
builddev.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
python3 envdev.py
|
||||||
|
PROJECT="utopia"
|
||||||
|
IMAGE="system-dev"
|
||||||
|
|
||||||
|
docker build -t docker.bigdatatech.vn/$PROJECT/$IMAGE:latest .
|
||||||
|
docker push docker.bigdatatech.vn/$PROJECT/$IMAGE:latest
|
||||||
14
components/Caption.vue
Normal file
14
components/Caption.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<span :class="`icon-text fsb-${props.size||17} ${props.type || 'has-text-findata'}`">
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
<SvgIcon id="ignore" v-bind="{name: 'right.svg', type: props.type? props.type.replace('has-text-', '') : null,
|
||||||
|
size: (props.size>=30? props.size*0.7 : props.size) || 20, alt: 'Mũi tên chỉ hướng'}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
var props = defineProps({
|
||||||
|
type: String,
|
||||||
|
size: Number,
|
||||||
|
title: String
|
||||||
|
})
|
||||||
|
</script>
|
||||||
70
components/Modal.vue
Normal file
70
components/Modal.vue
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modal is-active" @click="doClick">
|
||||||
|
<div class="modal-background" :style="`opacity:${count===0? 0.7 : 0.3} !important;'`"></div>
|
||||||
|
<div class="modal-card" :id="docid" :style="`width:${vWidth};`">
|
||||||
|
<header class="modal-card-head my-0 py-2" v-if="title">
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control is-expanded has-text-left">
|
||||||
|
<p class="fsb-18 has-text-dark" v-html="title"></p>
|
||||||
|
</div>
|
||||||
|
<div class="control has-text-right">
|
||||||
|
<button class="delete is-medium" @click="closeModal()"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body px-4 py-4" :style="`min-height:${height? height : '700px'}`">
|
||||||
|
<component :is="getComponent()" v-bind="props.vbind" @modalevent="modalEvent" @close="closeModal" />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, defineAsyncComponent } from 'vue'
|
||||||
|
import { useStore } from '~/stores/index'
|
||||||
|
const emit = defineEmits(['close', 'remove'])
|
||||||
|
const store = useStore()
|
||||||
|
const { $id } = useNuxtApp()
|
||||||
|
var props = defineProps({
|
||||||
|
component: String,
|
||||||
|
width: String,
|
||||||
|
height: String,
|
||||||
|
vbind: Object,
|
||||||
|
title: String
|
||||||
|
})
|
||||||
|
function getComponent() {
|
||||||
|
let arr = props.component.split('/')
|
||||||
|
if(arr.length===2) {
|
||||||
|
return defineAsyncComponent(() => import(`@/components/${arr[0]}/${arr[1]}.vue`))
|
||||||
|
} else {
|
||||||
|
return defineAsyncComponent(() => import(`@/components/${arr[0]}.vue`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const docid = $id()
|
||||||
|
const viewport = store.viewport
|
||||||
|
var title = props.title
|
||||||
|
var count = 0
|
||||||
|
var lock = false
|
||||||
|
var vWidth = viewport<=2? '100%' : props.width || '60%'
|
||||||
|
const closeModal = function() {
|
||||||
|
if(!lock) emit('close')
|
||||||
|
}
|
||||||
|
const modalEvent = function(ev) {
|
||||||
|
emit(ev.name, ev.data)
|
||||||
|
}
|
||||||
|
const doClick = function(e) {
|
||||||
|
//e.stopPropagation()
|
||||||
|
if(!e.srcElement.offsetParent) return
|
||||||
|
if(document.getElementById(docid)) {
|
||||||
|
if(!document.getElementById(docid).contains(e.target)) {
|
||||||
|
closeModal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(()=> {
|
||||||
|
window.addEventListener('keydown', (e) => {if(e.key==='Escape') closeModal()})
|
||||||
|
const collection = document.getElementsByClassName("modal-background")
|
||||||
|
count = collection.length
|
||||||
|
})
|
||||||
|
</script>
|
||||||
202
components/SearchBox.vue
Normal file
202
components/SearchBox.vue
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control has-icons-left is-expanded">
|
||||||
|
<div :class="`dropdown ${ pos || ''} ${focused? 'is-active' : ''}`" style="width: 100%;">
|
||||||
|
<div class="dropdown-trigger" style="width: 100%;">
|
||||||
|
<input :disabled="disabled" :class="`input ${error? 'is-danger' : ''} ${disabled? 'has-text-dark' : ''}`" type="text"
|
||||||
|
@focus="setFocus" @blur="lostFocus" @keyup.enter="pressEnter" @keyup="beginSearch" v-model="value" />
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" role="menu">
|
||||||
|
<div class="dropdown-content px-3" style="min-width: 250px;">
|
||||||
|
<p class="has-text-findata" v-if="data.length===0">Không có giá trị thỏa mãn</p>
|
||||||
|
<ScrollBox v-bind="{data: data, name: field, fontsize: 14, maxheight: '200px'}" @selected="choose" v-else></ScrollBox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="icon is-left">
|
||||||
|
<SvgIcon v-bind="{name: 'magnify.svg', type: 'gray', size: 22}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="control" v-if="viewaddon">
|
||||||
|
<button class="button is-dark px-2" @click="viewInfo()">
|
||||||
|
<SvgIcon v-bind="{name: 'view.svg', type: 'white', size: 22}"></SvgIcon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="control" v-if="addon">
|
||||||
|
<button class="button is-primary px-2" @click="addNew()">
|
||||||
|
<SvgIcon v-bind="{name: 'add1.png', type: 'white', size: 22}"></SvgIcon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Modal @dataevent="dataevent" @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const emit = defineEmits(['option', 'modalevent'])
|
||||||
|
import ScrollBox from '~/components/datatable/ScrollBox'
|
||||||
|
const { $nonAccent, $find, $getdata, $copy, $empty, $findapi, $findIndex} = useNuxtApp()
|
||||||
|
var props = defineProps({
|
||||||
|
api: String,
|
||||||
|
field: String,
|
||||||
|
column: Array,
|
||||||
|
first: Boolean,
|
||||||
|
optionid: Number,
|
||||||
|
filter: Object,
|
||||||
|
addon: Object,
|
||||||
|
viewaddon: Object,
|
||||||
|
position: String,
|
||||||
|
disabled: Boolean,
|
||||||
|
vdata: Object
|
||||||
|
})
|
||||||
|
var search = undefined
|
||||||
|
var data = ref([])
|
||||||
|
var timer = undefined
|
||||||
|
var value = ref()
|
||||||
|
var selected = undefined
|
||||||
|
var showmodal = ref()
|
||||||
|
var params = props.api? $findapi(props.api)['params'] : {}
|
||||||
|
var orgdata = undefined
|
||||||
|
var error = false
|
||||||
|
var focused = ref(false)
|
||||||
|
var count1 = 0
|
||||||
|
var count2 = 0
|
||||||
|
var pos = undefined
|
||||||
|
async function initData() {
|
||||||
|
getPos()
|
||||||
|
if(props.vdata) {
|
||||||
|
orgdata = $copy(props.vdata)
|
||||||
|
orgdata.map(v=>v.search = $nonAccent(v[props.field]))
|
||||||
|
}
|
||||||
|
if(props.first) {
|
||||||
|
data.value = orgdata? $copy(orgdata) : await getData()
|
||||||
|
if(props.optionid) {
|
||||||
|
let f = {}
|
||||||
|
f[props.field] = props.optionid
|
||||||
|
if(props.optionid>0) f = {id: props.optionid}
|
||||||
|
selected = $find(data.value, f)
|
||||||
|
if(selected && props.vdata) {
|
||||||
|
return value.value = selected[props.field]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(props.optionid) {
|
||||||
|
selected = await $getdata(props.api, {id: props.optionid}, undefined, true)
|
||||||
|
}
|
||||||
|
if(selected) doSelect(selected)
|
||||||
|
}
|
||||||
|
/*watch(()=> {
|
||||||
|
optionid: function(newVal) {
|
||||||
|
if(optionid) selected = $find(data, {id: optionid})
|
||||||
|
if(selected) doSelect(selected)
|
||||||
|
else value = undefined
|
||||||
|
}
|
||||||
|
filter: async function(newVal) {
|
||||||
|
data = await getData()
|
||||||
|
},
|
||||||
|
vdata: function(newval) {
|
||||||
|
if(newval) {
|
||||||
|
orgdata = $copy(props.vdata)
|
||||||
|
orgdata.map(v=>v.search = $nonAccent(v[field]))
|
||||||
|
data = $copy(orgdata)
|
||||||
|
selected = undefined
|
||||||
|
value = undefined
|
||||||
|
if(optionid) selected = $find(data, {id: optionid})
|
||||||
|
if(selected) doSelect(selected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}*/
|
||||||
|
function choose(v) {
|
||||||
|
focused.value = false
|
||||||
|
count1 = 0
|
||||||
|
count2 = 0
|
||||||
|
doSelect(v)
|
||||||
|
}
|
||||||
|
function setFocus() {
|
||||||
|
focused.value = true
|
||||||
|
count1 = 0
|
||||||
|
count2 = 0
|
||||||
|
}
|
||||||
|
function lostFocus() {
|
||||||
|
setTimeout(()=>{
|
||||||
|
focused.value = false
|
||||||
|
if($empty(value.value)) emit('option', null)
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
function pressEnter() {
|
||||||
|
if(data.length===0) return
|
||||||
|
choose(data[0])
|
||||||
|
}
|
||||||
|
function doClick() {
|
||||||
|
count1 += 1
|
||||||
|
}
|
||||||
|
function doSelect(option) {
|
||||||
|
if($empty(option)) return
|
||||||
|
selected = option
|
||||||
|
value.value = selected[props.field]
|
||||||
|
emit('option', option)
|
||||||
|
emit('modalevent', {name: 'option', data: option})
|
||||||
|
}
|
||||||
|
function findObject(val) {
|
||||||
|
let rows = $copy(orgdata)
|
||||||
|
if($empty(val)) data.value = rows
|
||||||
|
else {
|
||||||
|
let text = $nonAccent(val)
|
||||||
|
data.value = rows.filter(v=>v.search.toLowerCase().indexOf(text.toLowerCase())>=0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function getData() {
|
||||||
|
params.filter = props.filter
|
||||||
|
params.sort = '-id'
|
||||||
|
let data = await $getdata(props.api, undefined, params)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
async function getApi(val) {
|
||||||
|
if(props.vdata) return findObject(val)
|
||||||
|
let text = val? val.toLowerCase() : ''
|
||||||
|
let f = {}
|
||||||
|
props.column.map(v=>{
|
||||||
|
f[`${v}__icontains`] = text
|
||||||
|
})
|
||||||
|
params.filter_or = f
|
||||||
|
if(props.filter) params.filter = $copy(props.filter)
|
||||||
|
data.value = await $getdata(props.api, undefined, params)
|
||||||
|
}
|
||||||
|
function beginSearch(e) {
|
||||||
|
let val = e.target.value
|
||||||
|
search = val
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
|
timer = setTimeout(() => getApi(val), 150)
|
||||||
|
if($empty(val)) emit('option', null)
|
||||||
|
}
|
||||||
|
function addNew() {
|
||||||
|
showmodal.value = $copy(props.addon)
|
||||||
|
}
|
||||||
|
function dataevent(v) {
|
||||||
|
let idx = $findIndex(data.value, {id: v.id})
|
||||||
|
idx<0? data.value.push(v) : data.value[idx] = v
|
||||||
|
doSelect(v)
|
||||||
|
}
|
||||||
|
function viewInfo() {
|
||||||
|
if(!selected) return $dialog('Vui lòng lựa chọn trước khi xem thông tin.', 'Thông báo')
|
||||||
|
let copy = $copy(props.viewaddon)
|
||||||
|
copy.vbind = {row: selected}
|
||||||
|
showmodal.value = copy
|
||||||
|
}
|
||||||
|
function getPos() {
|
||||||
|
switch(props.position) {
|
||||||
|
case 'is-top-left':
|
||||||
|
pos = 'is-up is-left'
|
||||||
|
break;
|
||||||
|
case 'is-top-right':
|
||||||
|
pos = 'is-up is-right'
|
||||||
|
break;
|
||||||
|
case 'is-bottom-left':
|
||||||
|
pos = 'is-right'
|
||||||
|
break;
|
||||||
|
case 'is-bottom-right':
|
||||||
|
pos = 'is-right'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initData()
|
||||||
|
</script>
|
||||||
43
components/SvgIcon.vue
Normal file
43
components/SvgIcon.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<nuxt-img loading="lazy" :alt="alt" :class="`svg-${type || 'findata'}`" :style="`width: ${size || 26}px;`" :src="`/icon/${name || 'check.svg'}`"/>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['name', 'size', 'type', 'alt']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.svg-primary {
|
||||||
|
filter: brightness(0) saturate(100%) invert(48%) sepia(97%) saturate(2023%) hue-rotate(188deg) brightness(98%) contrast(89%);
|
||||||
|
}
|
||||||
|
.svg-danger {
|
||||||
|
filter: brightness(0) saturate(100%) invert(16%) sepia(100%) saturate(2983%) hue-rotate(351deg) brightness(101%) contrast(131%);
|
||||||
|
}
|
||||||
|
.svg-findata {
|
||||||
|
filter: brightness(0) saturate(100%) invert(61%) sepia(85%) saturate(1880%) hue-rotate(340deg) brightness(101%) contrast(101%);
|
||||||
|
}
|
||||||
|
.svg-warning {
|
||||||
|
filter: invert(86%) sepia(24%) saturate(5381%) hue-rotate(347deg) brightness(100%) contrast(103%);
|
||||||
|
}
|
||||||
|
.svg-blue {
|
||||||
|
filter: invert(9%) sepia(98%) saturate(7147%) hue-rotate(248deg) brightness(94%) contrast(145%);
|
||||||
|
}
|
||||||
|
.svg-green {
|
||||||
|
filter: invert(43%) sepia(99%) saturate(1326%) hue-rotate(88deg) brightness(97%) contrast(95%);
|
||||||
|
}
|
||||||
|
.svg-grey {
|
||||||
|
filter: invert(100%) sepia(0%) saturate(213%) hue-rotate(133deg) brightness(86%) contrast(93%);
|
||||||
|
}
|
||||||
|
.svg-gray {
|
||||||
|
filter: invert(35%) sepia(10%) saturate(18%) hue-rotate(338deg) brightness(97%) contrast(82%);
|
||||||
|
}
|
||||||
|
.svg-gray1 {
|
||||||
|
filter: invert(34%) sepia(6%) saturate(71%) hue-rotate(315deg) brightness(95%) contrast(88%);
|
||||||
|
}
|
||||||
|
.svg-white {
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
}
|
||||||
|
.svg-black {
|
||||||
|
filter: invert(0%) sepia(100%) saturate(0%) hue-rotate(235deg) brightness(107%) contrast(107%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
14
components/Tooltip.vue
Normal file
14
components/Tooltip.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<span class="tooltip">
|
||||||
|
<span v-html="props.html"></span>
|
||||||
|
<span class="tooltiptext" v-html="props.tooltip"></span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { defineAsyncComponent } from 'vue';
|
||||||
|
var props = defineProps({
|
||||||
|
html: String,
|
||||||
|
tooltip: String,
|
||||||
|
width: Number
|
||||||
|
})
|
||||||
|
</script>
|
||||||
122
components/TopMenu.vue
Executable file
122
components/TopMenu.vue
Executable file
@@ -0,0 +1,122 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="navbar is-fixed-top has-shadow px-3" role="navigation">
|
||||||
|
<div class="navbar-brand mr-3">
|
||||||
|
<span class="navbar-item mr-3">
|
||||||
|
<SvgIcon v-bind="{ name: 'dot.svg', size: 18, type: 'green' }"</SvgIcon>
|
||||||
|
<span class="fsb-20" style="color:#008000">{{$dayjs().format('DD/MM')}}</span>
|
||||||
|
</span>
|
||||||
|
<span class="navbar-item header-logo" aria-label="về trang chủ"></span>
|
||||||
|
<a role="button" class="navbar-burger" id="burger" aria-label="menu" aria-expanded="false" data-target="navMenu" @click="handleClick()">
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-menu" id="navMenu">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<template v-for="(v,i) in leftmenu" :key="i" :id="v.code">
|
||||||
|
<a class="navbar-item" v-if="!v.submenu" @click="changeTab(v)">
|
||||||
|
<span :class="`fsb-17 ${currentTab.code===v.code? 'activetab' : ''}`">{{ v[lang] }}</span>
|
||||||
|
</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable" v-else>
|
||||||
|
<a class="navbar-item" @click="changeTab(v)">
|
||||||
|
<span :class="`icon-text ${currentTab.code===v.code? 'activetab' : ''}`">
|
||||||
|
<span class="fsb-17">{{v[lang]}}</span>
|
||||||
|
<SvgIcon style="padding-top:4px;" v-bind="{name: 'down2.svg',
|
||||||
|
type: currentTab.code===v.code? 'white' : 'dark', size: 15}">
|
||||||
|
</SvgIcon>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div class="navbar-dropdown has-background-light">
|
||||||
|
<a
|
||||||
|
class="navbar-item has-background-light fs-15 has-text-black py-1 border-bottom"
|
||||||
|
v-for="x in v.submenu"
|
||||||
|
@click="changeTab(v, x)"
|
||||||
|
>
|
||||||
|
{{ x[lang] }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<!-- <span class="navbar-item">
|
||||||
|
<LanguageToggle @langChanged="changeLang"></LanguageToggle>
|
||||||
|
</span> -->
|
||||||
|
<a class="navbar-item" @click="changeTab(tabConfig)">
|
||||||
|
<SvgIcon v-bind="{name: 'configuration.svg', type: 'findata', size: 24}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a class="navbar-item" @click="openProfile()" v-if="avatar">
|
||||||
|
<Avatarbox v-bind="avatar" ></Avatarbox>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useStore } from '~/stores/index'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const router = useRouter()
|
||||||
|
const store = useStore()
|
||||||
|
const route = useRoute()
|
||||||
|
const emit = defineEmits(['changetab', 'langChanged'])
|
||||||
|
const { $clone, $find, $filter } = useNuxtApp()
|
||||||
|
var lang = ref(store.lang)
|
||||||
|
var menu = $filter(store.common, {category: 'topmenu'})
|
||||||
|
menu.map(v=>{
|
||||||
|
let arr = $filter(store.common, {category: 'submenu', classify: v.code})
|
||||||
|
v.submenu = arr.length>0? arr : null
|
||||||
|
})
|
||||||
|
var leftmenu = $filter(store.common, {category: 'topmenu', classify: 'left'})
|
||||||
|
var currentTab = ref(leftmenu[0])
|
||||||
|
var subTab = ref()
|
||||||
|
var tabConfig = $find(menu, {code: 'configuration'})
|
||||||
|
var avatar = ref()
|
||||||
|
var isAdmin = ref()
|
||||||
|
const handleClick = function() {
|
||||||
|
const target = document.getElementById('burger');
|
||||||
|
target.classList.toggle('is-active');
|
||||||
|
const target1 = document.getElementById('navMenu');
|
||||||
|
target1.classList.toggle('is-active');
|
||||||
|
}
|
||||||
|
const closeMenu = function() {
|
||||||
|
if(!document) return
|
||||||
|
const target = document.getElementById('burger');
|
||||||
|
const target1 = document.getElementById('navMenu');
|
||||||
|
if(!target) return
|
||||||
|
if(target.classList.contains('is-active')) {
|
||||||
|
target.classList.remove('is-active')
|
||||||
|
target1.classList.remove('is-active')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function changeLang(val) {
|
||||||
|
lang.value = val
|
||||||
|
emit('langChanged')
|
||||||
|
}
|
||||||
|
function changeTab(tab, subtab) {
|
||||||
|
currentTab.value = tab
|
||||||
|
subTab.value = subtab
|
||||||
|
emit('changetab', tab, subtab)
|
||||||
|
closeMenu()
|
||||||
|
let query = subtab? {tab: tab.code, subtab: subtab.code} : {tab: tab.code}
|
||||||
|
router.push({query: query})
|
||||||
|
}
|
||||||
|
function openProfile() {
|
||||||
|
let modal = {component: 'user/Profile', width: '1100px', height: '400px', title: 'User profile'}
|
||||||
|
store.commit('showmodal', modal)
|
||||||
|
}
|
||||||
|
let found = route.query.tab? $find(menu, {code: route.query.tab}) : null
|
||||||
|
changeTab(found || leftmenu[0])
|
||||||
|
onMounted(()=> {
|
||||||
|
if(!store.login) return
|
||||||
|
avatar.value = {image: null, text: store.login.fullname.substring(0,1).toUpperCase(), size: 'two', type: 'findata'}
|
||||||
|
isAdmin.value = store.login.type__code==='admin'
|
||||||
|
})
|
||||||
|
watch(() => store.login, (newVal, oldVal) => {
|
||||||
|
if(!newVal) return
|
||||||
|
avatar.value = {image: null, text: store.login.fullname.substring(0,1).toUpperCase(), size: 'two', type: 'findata'}
|
||||||
|
isAdmin.value = store.login.type__code==='admin'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
15
components/common/Avatarbox.vue
Normal file
15
components/common/Avatarbox.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="`cbox-${type}-${size} mx-0 px-0`" @click="$emit('justclick')" :style="image? 'border: none' : ''">
|
||||||
|
<figure class="image" v-if="image">
|
||||||
|
<img class="is-rounded" :src="`${$path()}download?name=${image}`">
|
||||||
|
</figure>
|
||||||
|
<div v-else>
|
||||||
|
<span>{{text}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['text', 'image', 'type', 'size']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
36
components/common/Editor.vue
Normal file
36
components/common/Editor.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<ClientOnly>
|
||||||
|
<QuillEditor v-model:content="content" content-type="html" theme="snow" :toolbar="toolbarOptions" @text-change="textChange"
|
||||||
|
style="font-size: 16px; height: 450px;" />
|
||||||
|
</ClientOnly>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { QuillEditor } from '@vueup/vue-quill'
|
||||||
|
import '@vueup/vue-quill/dist/vue-quill.snow.css'
|
||||||
|
const emit = defineEmits(['content', 'modalevent'])
|
||||||
|
var props = defineProps({
|
||||||
|
text: String,
|
||||||
|
row: Object,
|
||||||
|
pagename: String,
|
||||||
|
api: String
|
||||||
|
})
|
||||||
|
// Custom toolbar options
|
||||||
|
const toolbarOptions = [
|
||||||
|
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
||||||
|
['bold', 'italic', 'underline', 'strike'],
|
||||||
|
['blockquote', 'code-block'],
|
||||||
|
[{ header: 1 }, { header: 2 }],
|
||||||
|
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||||
|
[{ script: 'sub' }, { script: 'super' }],
|
||||||
|
[{ indent: '-1' }, { indent: '+1' }],
|
||||||
|
[{ color: [] }, { background: [] }],
|
||||||
|
[{ align: [] }],
|
||||||
|
['clean'],
|
||||||
|
['link', 'image', 'video']
|
||||||
|
]
|
||||||
|
var content = props.text
|
||||||
|
function textChange() {
|
||||||
|
emit('content', content)
|
||||||
|
emit('modalevent', {name: 'content', data: content})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
37
components/common/InputEmail.vue
Normal file
37
components/common/InputEmail.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div class="control has-icons-left">
|
||||||
|
<input :class="`input ${error? 'is-danger' : ''} ${disabled? 'has-text-black' : ''}`" type="text"
|
||||||
|
:placeholder="placeholder || ''" v-model="value" @keyup="doCheck" :disabled="disabled || false">
|
||||||
|
<span class="icon is-left">
|
||||||
|
<SvgIcon v-bind="{name: 'email.svg', type: 'gray', size: 21}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['record', 'attr', 'placeholder', 'disabled'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: this.record[this.attr]? this.$copy(this.record[this.attr]) : undefined,
|
||||||
|
error: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
record: function(newVal) {
|
||||||
|
this.value = this.record[this.attr]? this.$copy(this.record[this.attr]) : undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
doCheck() {
|
||||||
|
if(this.$empty(this.value)) {
|
||||||
|
this.value = undefined
|
||||||
|
this.error = false
|
||||||
|
return this.$emit('email', null)
|
||||||
|
}
|
||||||
|
let check = this.$errEmail(this.value)
|
||||||
|
this.error = check? true : false
|
||||||
|
this.$emit('email', this.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
40
components/common/InputNumber.vue
Normal file
40
components/common/InputNumber.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<div class="control has-icons-left">
|
||||||
|
<input :class="`input ${disabled? 'has-text-black' : ''}`" type="text" :placeholder="placeholder || ''" v-model="value" @keyup="doCheck"
|
||||||
|
:disabled="disabled || false">
|
||||||
|
<span class="icon is-left">
|
||||||
|
<SvgIcon v-bind="{name: 'calculator.svg', type: 'gray', size: 22}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['record', 'attr', 'placeholder', 'disabled'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: this.record[this.attr]? this.$copy(this.record[this.attr]) : undefined,
|
||||||
|
timer: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if(this.value) this.value = this.$numtoString(this.value)
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
record: function(newVal) {
|
||||||
|
this.value = this.$numtoString(this.record[this.attr])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
doCheck() {
|
||||||
|
if(this.timer) clearTimeout(this.timer)
|
||||||
|
this.timer = setTimeout(()=>this.checkChange(), 500)
|
||||||
|
},
|
||||||
|
checkChange() {
|
||||||
|
if(!this.$empty(this.value)) {
|
||||||
|
this.value = this.$numtoString(this.$formatNumber(this.value))
|
||||||
|
this.$emit('number', this.$formatNumber(this.value))
|
||||||
|
} else this.$formatNumber('number', null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
37
components/common/InputPhone.vue
Normal file
37
components/common/InputPhone.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div class="control has-icons-left">
|
||||||
|
<input :class="`input ${error? 'is-danger' : ''} ${disabled? 'has-text-black' : ''}`" type="text"
|
||||||
|
:placeholder="placeholder || ''" v-model="value" @keyup="doCheck" :disabled="disabled || false">
|
||||||
|
<span class="icon is-left">
|
||||||
|
<SvgIcon v-bind="{name: 'phone.png', type: 'gray', size: 20}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['record', 'attr', 'placeholder', 'disabled'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: this.record[this.attr]? this.$copy(this.record[this.attr]) : undefined,
|
||||||
|
error: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
record: function(newVal) {
|
||||||
|
this.value = this.record[this.attr]? this.$copy(this.record[this.attr]) : undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
doCheck() {
|
||||||
|
if(this.$empty(this.value)) {
|
||||||
|
this.value = undefined
|
||||||
|
this.error = false
|
||||||
|
return this.$emit('phone', null)
|
||||||
|
}
|
||||||
|
let check = this.$errPhone(this.value)
|
||||||
|
this.error = check? true : false
|
||||||
|
this.$emit('phone', this.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
107
components/common/JobView.vue
Normal file
107
components/common/JobView.vue
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="columns is-multiline mx-0">
|
||||||
|
<div class="column is-3">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{findLang('name')}}<b class="ml-1 has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input has-text-black" type="text" placeholder="" disabled v-model="record.fullname">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{findLang('job')}}<b class="ml-1 has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input has-text-black" type="text" placeholder="" disabled v-model="record.job__title">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-3">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{findLang('phone')}}<b class="ml-1 has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input has-text-black" type="text" placeholder="" disabled v-model="record.phone">
|
||||||
|
</div>
|
||||||
|
<p class="help is-danger" v-if="errors.phone">{{ errors.phone }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Email<b class="ml-1 has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input has-text-black" type="text" disabled placeholder="" v-model="record.email">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{findLang('address')}}<b class="ml-1 has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input has-text-black" type="text" disabled placeholder="" v-model="record.address">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-3">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{findLang('apply-date')}}<b class="ml-1 has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input has-text-black" type="text" placeholder="" disabled :value="$dayjs(record.create_time).format('DD/MM/YYYY hh:mm')">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-9">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Link<b class="ml-1 has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" disabled placeholder="" v-model="record.link">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-3">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{findLang('status')}}<b class="ml-1 has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<SearchBox v-bind="{api:'applyresult', field: store.lang==='en'? 'en' : 'name', column:[store.lang==='en'? 'en' : 'name'],
|
||||||
|
first: true, optionid: record.result, sort: 'name', position: 'is-top-left'}" @option="selected('_result', $event)"></SearchBox>
|
||||||
|
</div>
|
||||||
|
<p class="help is-danger" v-if="errors.url_web">{{ errors.url_web }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12">
|
||||||
|
<div class="mt-3">
|
||||||
|
<button class="button is-primary has-text-white" @click="save()">{{findLang('save')}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { useStore } from '~/stores/index'
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const store = useStore()
|
||||||
|
return { store }
|
||||||
|
},
|
||||||
|
props: ['pagename', 'row'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
errors: {},
|
||||||
|
record: this.$copy(this.row || {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
findLang(code) {
|
||||||
|
let found = this.$find(this.store.common, {code: code})
|
||||||
|
return found? found[this.store.lang] : null
|
||||||
|
},
|
||||||
|
selected(attr, obj) {
|
||||||
|
this.record.result = obj.id
|
||||||
|
},
|
||||||
|
async save() {
|
||||||
|
let found = this.$findapi('jobapply')
|
||||||
|
await this.$updaterow('jobapply', this.record, found.params.values, this.pagename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
121
components/common/NoteInfo.vue
Normal file
121
components/common/NoteInfo.vue
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<template v-if="data">
|
||||||
|
<article class="message is-findata" v-if="data.length===0">
|
||||||
|
<div class="message-body py-2 fs-16">
|
||||||
|
Chưa có <b>ghi chú</b> nào được lưu
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<template v-else>
|
||||||
|
<article class="media" v-for="(v,i) in data">
|
||||||
|
<figure class="media-left">
|
||||||
|
<Avatarbox v-bind="{text: v.user__fullname.substring(0,1).toUpperCase(), size: 'two', type: 'findata'}" />
|
||||||
|
</figure>
|
||||||
|
<div class="media-content">
|
||||||
|
<div>
|
||||||
|
<p class="fs-15">
|
||||||
|
{{ v.detail }}
|
||||||
|
</p>
|
||||||
|
<p class="mt-1 has-text-grey">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span>{{v.user__fullname}}</span>
|
||||||
|
<span class="ml-3">{{ $dayjs(v['create_time']).fromNow(true) }}</span>
|
||||||
|
<template v-if="login.id===v.user">
|
||||||
|
<a class="ml-3" @click="edit(v)">
|
||||||
|
<SvgIcon v-bind="{name: 'pen1.svg', type: 'gray', size: 22}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a class="ml-3" @click="askConfirm(v, i)">
|
||||||
|
<SvgIcon v-bind="{name: 'bin.svg', type: 'gray', size: 22}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<div class="field is-grouped mt-3">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<textarea class="textarea" rows="1" placeholder="Viết ghi chú tại đây" v-model="detail"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-primary has-text-white" @click="save()">Lưu</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal" @confirm="confirm()"></Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['row', 'api', 'pagename'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
data: undefined,
|
||||||
|
detail: undefined,
|
||||||
|
vbind2: {image: undefined, text: 'ABC', size: 'two', type: 'findata'},
|
||||||
|
current: undefined,
|
||||||
|
showmodal: undefined,
|
||||||
|
obj: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
if(!this.row) return
|
||||||
|
this.data = await this.$getdata(this.api, {ref: this.row.id})
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
login: {
|
||||||
|
get: function() {return this.$store.state.login},
|
||||||
|
set: function(val) {this.$store.commit("updateLogin", {login: val})}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async save() {
|
||||||
|
if(this.$empty(this.detail)) return this.$snackbar('Chưa nhập nội dung ghi chú')
|
||||||
|
let data = {user: this.$store.state.login.id, detail: this.detail, ref: this.row.id}
|
||||||
|
if(this.current) {
|
||||||
|
data = this.$copy(this.current)
|
||||||
|
data.detail = this.detail
|
||||||
|
}
|
||||||
|
let rs = data.id? await this.$updateapi(this.api, data) : await this.$insertapi(this.api, data)
|
||||||
|
if(!rs) return
|
||||||
|
this.detail = undefined
|
||||||
|
if(this.current) {
|
||||||
|
this.current = undefined
|
||||||
|
let idx = this.$findIndex(this.data, {id: rs.id})
|
||||||
|
this.$set(this.data, idx, rs)
|
||||||
|
} else {
|
||||||
|
this.data.push(rs)
|
||||||
|
let rows = this.$copy(this.$store.state[this.pagename].data)
|
||||||
|
let idx = this.$findIndex(rows, {id: this.row.id})
|
||||||
|
let copy = this.$copy(this.row)
|
||||||
|
copy.count_note += 1
|
||||||
|
rows[idx] = copy
|
||||||
|
this.$store.commit('updateState', {name: this.pagename, key: 'update', data: {data: rows}})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edit(v) {
|
||||||
|
this.current = this.$copy(v)
|
||||||
|
this.detail = v.detail
|
||||||
|
},
|
||||||
|
askConfirm(v, i) {
|
||||||
|
this.obj = {v: v, i: i}
|
||||||
|
this.showmodal = {component: `dialog/Confirm`,vbind: {content: 'Bạn có muốn xóa ghi chú này không?', duration: 10},
|
||||||
|
title: 'Xóa ghi chú', width: '500px', height: '100px'}
|
||||||
|
},
|
||||||
|
async confirm() {
|
||||||
|
let v = this.obj.v
|
||||||
|
let i = this.obj.i
|
||||||
|
let rs = await this.$deleteapi(this.api, v.id)
|
||||||
|
if(rs==='error') return
|
||||||
|
this.$delete(this.data, i)
|
||||||
|
let rows = this.$copy(this.$store.state[this.pagename].data)
|
||||||
|
let idx = this.$findIndex(rows, {id: this.row.id})
|
||||||
|
let copy = this.$copy(this.row)
|
||||||
|
copy.count_note -= 1
|
||||||
|
rows[idx] = copy
|
||||||
|
this.$store.commit('updateState', {name: this.pagename, key: 'update', data: {data: rows}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
14
components/common/Notebox.vue
Normal file
14
components/common/Notebox.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<span class="dot-primary" @click="doClick()">{{ row.count_note || '+' }}</span>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['row', 'api', 'pagename'],
|
||||||
|
methods: {
|
||||||
|
doClick() {
|
||||||
|
let obj = {component: 'common/NoteInfo', title: 'Ghi chú', width: '50%', vbind: {row: this.row, api: this.api, pagename: this.pagename}}
|
||||||
|
this.$emit('open', {name: 'dataevent', data: {modal: obj}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
54
components/common/Phone.vue
Normal file
54
components/common/Phone.vue
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p class="fsb-30">{{ text }}
|
||||||
|
<a class="ml-5" @click="copy()">
|
||||||
|
<SvgIcon v-bind="{name: 'copy.svg', type: 'gray', size: 24}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="buttons mt-5 pt-2">
|
||||||
|
<button class="button is-primary has-text-white mr" @click="call()">Call</button>
|
||||||
|
<button class="button is-primary has-text-white mr-2" @click="sms()">SMS</button>
|
||||||
|
<button class="button is-dark mr-2" @click="openZalo()">Zalo</button>
|
||||||
|
</p>
|
||||||
|
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['row', 'pagename'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
text: undefined,
|
||||||
|
phone: this.row.customer__phone || this.row.party__phone || this.row.phone,
|
||||||
|
showmodal: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
var format = function(s) {
|
||||||
|
return `${s.slice(0,3)} ${s.slice(3,6)} ${s.slice(6, 20)}`
|
||||||
|
}
|
||||||
|
this.text = format(this.phone)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
call() {
|
||||||
|
window.open(`tel:${this.phone}`)
|
||||||
|
},
|
||||||
|
sms() {
|
||||||
|
window.open(`sms:${this.phone}`)
|
||||||
|
},
|
||||||
|
sendSms() {
|
||||||
|
let api = this.row.code.indexOf('CN')>=0? 'customersms' : undefined
|
||||||
|
if(this.row.code.indexOf('LN')>=0) api = 'loansms'
|
||||||
|
else if(this.row.code.indexOf('TS')>=0) api = 'collateralsms'
|
||||||
|
this.showmodal = {component: 'user/Sms', title: 'Nhắn tin SMS', width: '50%', height: '400px',
|
||||||
|
vbind: {row: this.row, pagename: this.pagename, api: api}}
|
||||||
|
},
|
||||||
|
copy() {
|
||||||
|
this.$copyToClipboard(this.phone)
|
||||||
|
},
|
||||||
|
openZalo() {
|
||||||
|
window.open(`https://zalo.me/${this.phone}`, '_blank')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
540
components/datatable/ContextMenu.vue
Normal file
540
components/datatable/ContextMenu.vue
Normal file
@@ -0,0 +1,540 @@
|
|||||||
|
<template>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a
|
||||||
|
class="mr-4"
|
||||||
|
@click="checkFilter() ? false : $emit('modalevent', { name: 'dosort', data: 'az' })"
|
||||||
|
>
|
||||||
|
<SvgIcon
|
||||||
|
v-bind="{ name: 'az.svg', type: checkFilter() ? 'grey' : 'primary', size: 22 }"
|
||||||
|
></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
v-html="'Sắp xếp tăng dần'"
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a
|
||||||
|
class="mr-4"
|
||||||
|
@click="checkFilter() ? false : $emit('modalevent', { name: 'dosort', data: 'za' })"
|
||||||
|
>
|
||||||
|
<SvgIcon
|
||||||
|
v-bind="{ name: 'az.svg', type: checkFilter() ? 'grey' : 'primary', size: 22 }"
|
||||||
|
></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Sắp xếp giảm dần</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="moveLeft()">
|
||||||
|
<SvgIcon v-bind="{ name: 'left5.png', type: 'primary', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Chuyển cột sang trái</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="moveRight()">
|
||||||
|
<SvgIcon v-bind="{ name: 'right5.png', type: 'primary', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Chuyển cột sang phải</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="resizeWidth()">
|
||||||
|
<SvgIcon v-bind="{ name: 'thick.svg', type: 'primary', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Tăng độ rộng cột</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="resizeWidth(true)">
|
||||||
|
<SvgIcon v-bind="{ name: 'thin.svg', type: 'primary', size: 23 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Giảm độ rộng cột</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="hideField()">
|
||||||
|
<SvgIcon v-bind="{ name: 'eye-off.svg', type: 'primary', size: 23 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Ẩn cột</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<!-- <template v-if="store.login ? store.login.is_admin : false"> -->
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="currentField.mandatory ? false : doRemove()">
|
||||||
|
<SvgIcon v-bind="{ name: 'bin.svg', type: 'primary', size: 23 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Xóa cột</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a
|
||||||
|
class="mr-4"
|
||||||
|
:class="currentField.format === 'number' ? null : 'has-text-grey-light'"
|
||||||
|
@click="
|
||||||
|
currentField.format === 'number'
|
||||||
|
? $emit('modalevent', { name: 'copyfield', data: currentField })
|
||||||
|
: false
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<SvgIcon v-bind="{ name: 'copy.svg', type: 'primary', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Sao chép cột</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="fieldList()">
|
||||||
|
<SvgIcon v-bind="{ name: 'menu4.png', type: 'primary', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Danh sách cột</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="createField()">
|
||||||
|
<SvgIcon v-bind="{ name: 'add.png', type: 'primary', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Tạo cột mới</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="tableOption()">
|
||||||
|
<SvgIcon v-bind="{ name: 'more.svg', type: 'primary', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Tùy chọn bảng</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-4" @click="saveSetting()">
|
||||||
|
<SvgIcon v-bind="{ name: 'save.svg', type: 'primary', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Lưu thiết lập</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<!-- </template> -->
|
||||||
|
<div class="panel-tabs mb-2">
|
||||||
|
<a
|
||||||
|
v-for="(v, i) in getMenu().filter((x) =>
|
||||||
|
currentField.format === 'number'
|
||||||
|
? currentField.formula
|
||||||
|
? true
|
||||||
|
: x.code !== 'formula'
|
||||||
|
: !['filter', 'formula'].find((y) => y === x.code)
|
||||||
|
)"
|
||||||
|
:key="i"
|
||||||
|
:class="selectTab.code === v.code ? 'is-active' : 'has-text-primary'"
|
||||||
|
@click="changeTab(v)"
|
||||||
|
>
|
||||||
|
{{ v.name }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div v-if="currentTab === 'detail'">
|
||||||
|
<p class="fs-14 mt-3">
|
||||||
|
<strong> Tên trường: </strong> {{ currentField.name }}
|
||||||
|
<a @click="copyContent(currentField.name)">
|
||||||
|
<span class="tooltip">
|
||||||
|
<SvgIcon
|
||||||
|
class="ml-1"
|
||||||
|
v-bind="{ name: 'copy.svg', type: 'primary', size: 16 }"
|
||||||
|
></SvgIcon>
|
||||||
|
<span
|
||||||
|
class="tooltiptext"
|
||||||
|
style="top: 60%; bottom: unset; min-width: max-content; left: 25px"
|
||||||
|
>Copy</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<label class="label fs-14 mt-3">Mô tả<span class="has-text-danger"> *</span></label>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<input
|
||||||
|
class="input fs-14"
|
||||||
|
type="text"
|
||||||
|
@change="changeLabel($event.target.value)"
|
||||||
|
v-model="label"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button" @click="editLabel()">
|
||||||
|
<SvgIcon v-bind="{ name: 'pen.svg', type: 'dark', size: 19 }"></SvgIcon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help is-danger" v-if="errors.find((v) => v.name === 'label')">
|
||||||
|
{{ errors.find((v) => v.name === "label").msg }}
|
||||||
|
</p>
|
||||||
|
<div class="field mt-3">
|
||||||
|
<label class="label fs-14"
|
||||||
|
>Kiểu dữ liệu<span class="has-text-danger"> * </span></label
|
||||||
|
>
|
||||||
|
<div class="control fs-14">
|
||||||
|
<span class="mr-4" v-for="(v, i) in datatype">
|
||||||
|
<span class="icon-text" v-if="radioType === v">
|
||||||
|
<SvgIcon
|
||||||
|
v-bind="{ name: 'radio-checked.svg', type: 'gray', size: 22 }"
|
||||||
|
></SvgIcon>
|
||||||
|
</span>
|
||||||
|
{{ v.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal" v-if="currentField.format === 'number'">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14"
|
||||||
|
>Đơn vị <span class="has-text-danger"> * </span>
|
||||||
|
</label>
|
||||||
|
<div class="control">
|
||||||
|
<SearchBox
|
||||||
|
v-bind="{
|
||||||
|
vdata: moneyunit,
|
||||||
|
field: 'name',
|
||||||
|
column: ['name'],
|
||||||
|
first: true,
|
||||||
|
position: 'is-top-left',
|
||||||
|
}"
|
||||||
|
@option="selected('_account', $event)"
|
||||||
|
></SearchBox>
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find((v) => v.name === 'unit')">
|
||||||
|
{{ errors.find((v) => v.name === "unit").msg }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field is-narrow">
|
||||||
|
<label class="label fs-14">Phần thập phân</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
placeholder=""
|
||||||
|
v-model="decimal"
|
||||||
|
@input="changeDecimal($event.target.value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal mt-3">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14">Định dạng nâng cao</label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<span
|
||||||
|
class="mr-4"
|
||||||
|
v-for="(v, i) in colorchoice.filter((v) => v.code !== 'condition')"
|
||||||
|
>
|
||||||
|
<a class="icon-text" @click="changeTemplate(v)">
|
||||||
|
<SvgIcon
|
||||||
|
v-bind="{
|
||||||
|
name: `radio-${radioTemplate === v.code ? '' : 'un'}checked.svg`,
|
||||||
|
type: 'gray',
|
||||||
|
size: 22,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
</SvgIcon>
|
||||||
|
</a>
|
||||||
|
{{ v.name }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-3" v-if="radioTemplate === 'option'">
|
||||||
|
<button class="button is-primary is-small has-text-white" @click="showSidebar()">
|
||||||
|
<span class="fs-14">{{
|
||||||
|
`${currentField.template ? "Sửa" : "Tạo"} định dạng`
|
||||||
|
}}</span>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="currentTab === 'value'">
|
||||||
|
<ScrollBox
|
||||||
|
v-bind="{
|
||||||
|
data: props.filterData,
|
||||||
|
name: props.field.name,
|
||||||
|
maxheight: '380px',
|
||||||
|
perpage: 20,
|
||||||
|
}"
|
||||||
|
@selected="doSelect"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Modal
|
||||||
|
v-bind="showmodal"
|
||||||
|
v-if="showmodal"
|
||||||
|
@label="changeLabel"
|
||||||
|
@updatefields="updateFields"
|
||||||
|
@close="close"
|
||||||
|
></Modal>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { useStore } from "@/stores/index";
|
||||||
|
import ScrollBox from "~/components/datatable/ScrollBox";
|
||||||
|
const store = useStore();
|
||||||
|
const {
|
||||||
|
$copy,
|
||||||
|
$stripHtml,
|
||||||
|
$clone,
|
||||||
|
$arrayMove,
|
||||||
|
$snackbar,
|
||||||
|
$copyToClipboard,
|
||||||
|
} = useNuxtApp();
|
||||||
|
var props = defineProps({
|
||||||
|
pagename: String,
|
||||||
|
field: Object,
|
||||||
|
filters: Object,
|
||||||
|
filterData: Object,
|
||||||
|
width: String,
|
||||||
|
});
|
||||||
|
const emit = defineEmits(["modalevent", "changepos", "close"]);
|
||||||
|
var colorchoice = store.colorchoice;
|
||||||
|
var errors = [];
|
||||||
|
var currentTab = ref("value");
|
||||||
|
var currentField = $copy(props.field);
|
||||||
|
var pagedata = store[props.pagename];
|
||||||
|
var fields = [];
|
||||||
|
var label = currentField.label;
|
||||||
|
var showmodal = ref();
|
||||||
|
const checkFilter = function () {};
|
||||||
|
const getMenu = function () {
|
||||||
|
let field = currentField;
|
||||||
|
field.disable = "display,tooltip";
|
||||||
|
let arr = field.disable ? field.disable.split(",") : undefined;
|
||||||
|
let array = arr
|
||||||
|
? store.menuchoice.filter((v) => arr.findIndex((x) => x === v.code) < 0)
|
||||||
|
: store.menuchoice;
|
||||||
|
//if (store.login ? !(store.login.is_admin === false) : true) array = [array[0]];
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
var selectTab = getMenu()[0];
|
||||||
|
var datatype = store.datatype;
|
||||||
|
var current = 1;
|
||||||
|
var value1 = undefined;
|
||||||
|
var value2 = undefined;
|
||||||
|
var moneyunit = store.moneyunit;
|
||||||
|
var radioType = store.datatype.find((v) => v.code === currentField.format);
|
||||||
|
var selectUnit =
|
||||||
|
currentField.format === "number"
|
||||||
|
? moneyunit.find((v) => v.detail === currentField.unit)
|
||||||
|
: undefined;
|
||||||
|
var bgcolor = undefined;
|
||||||
|
var radioBGcolor = colorchoice.find((v) => v.code === "none");
|
||||||
|
var color = undefined;
|
||||||
|
var radioColor = colorchoice.find((v) => v.code === "none");
|
||||||
|
var textsize = undefined;
|
||||||
|
var radioSize = colorchoice.find((v) => v.code === "none");
|
||||||
|
var minwidth = undefined;
|
||||||
|
var radioWidth = colorchoice.find((v) => v.code === "none");
|
||||||
|
var radioMaxWidth = colorchoice.find((v) => v.code === "none");
|
||||||
|
var maxwidth = undefined;
|
||||||
|
var selectAlign = undefined;
|
||||||
|
var radioAlign = colorchoice.find((v) => v.code === "none");
|
||||||
|
var radioTemplate = ref(
|
||||||
|
colorchoice.find((v) => v.code === (currentField.template ? "option" : "none"))["code"]
|
||||||
|
);
|
||||||
|
var selectPlacement = store.placement.find((v) => v.code === "is-right");
|
||||||
|
var selectScheme = store.colorscheme.find((v) => v.code === "is-primary");
|
||||||
|
var radioTooltip = store.colorchoice.find((v) => v.code === "none");
|
||||||
|
var selectField = undefined;
|
||||||
|
var tags = currentField.tags
|
||||||
|
? currentField.tags.map((v) => fields.find((x) => x.name === v))
|
||||||
|
: [];
|
||||||
|
var formula = currentField.formula ? currentField.formula : undefined;
|
||||||
|
var decimal = currentField.decimal;
|
||||||
|
let shortmenu = store.menuchoice.filter((x) =>
|
||||||
|
currentField.format === "number"
|
||||||
|
? currentField.formula
|
||||||
|
? true
|
||||||
|
: x.code !== "formula"
|
||||||
|
: !["filter", "formula"].find((y) => y === x.code)
|
||||||
|
);
|
||||||
|
var selectTab = shortmenu.find((v) => selectTab.code === v.code)
|
||||||
|
? selectTab
|
||||||
|
: menuchoice.find((v) => v.code === "value");
|
||||||
|
var search = undefined;
|
||||||
|
// if(selectTab.code==='value') {
|
||||||
|
// let self = this
|
||||||
|
// setTimeout(function() {self.$refs[currentField.name]? self.$refs[currentField.name].focus() : false}, 50)
|
||||||
|
// }
|
||||||
|
//==============================================================
|
||||||
|
function moveLeft() {
|
||||||
|
let copy = $clone(pagedata);
|
||||||
|
let i = copy.fields.findIndex((v) => v.name === props.field.name);
|
||||||
|
let idx = i - 1 >= 0 ? i - 1 : copy.fields.length - 1;
|
||||||
|
$arrayMove(copy.fields, i, idx);
|
||||||
|
copy.update = { fields: copy.fields };
|
||||||
|
store.commit(props.pagename, copy);
|
||||||
|
emit("changepos");
|
||||||
|
}
|
||||||
|
function moveRight() {
|
||||||
|
let copy = $clone(pagedata);
|
||||||
|
let i = copy.fields.findIndex((v) => v.name === props.field.name);
|
||||||
|
let idx = copy.fields.length - 1 > i ? i + 1 : 0;
|
||||||
|
$arrayMove(copy.fields, i, idx);
|
||||||
|
copy.update = { fields: copy.fields };
|
||||||
|
store.commit(props.pagename, copy);
|
||||||
|
emit("changepos");
|
||||||
|
}
|
||||||
|
function hideField() {
|
||||||
|
let copy = $clone(store[props.pagename]);
|
||||||
|
let found = copy.fields.find((v) => v.name === props.field.name);
|
||||||
|
found.show = false;
|
||||||
|
copy.update = { fields: copy.fields };
|
||||||
|
store.commit(props.pagename, copy);
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
function doRemove() {
|
||||||
|
let copy = $clone(store[props.pagename]);
|
||||||
|
let idx = copy.fields.findIndex((v) => v.name === props.field.name);
|
||||||
|
copy.fields.splice(idx, 1);
|
||||||
|
copy.update = { fields: copy.fields };
|
||||||
|
store.commit(props.pagename, copy);
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
function fieldList() {
|
||||||
|
showmodal.value = {
|
||||||
|
component: "datatable/TableOption",
|
||||||
|
vbind: { pagename: props.pagename },
|
||||||
|
title: "Danh sách cột",
|
||||||
|
width: "50%",
|
||||||
|
height: "630px",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function tableOption() {
|
||||||
|
showmodal.value = {
|
||||||
|
component: "datatable/TableSetting",
|
||||||
|
vbind: { pagename: props.pagename },
|
||||||
|
title: "Tùy chọn bảng",
|
||||||
|
width: "40%",
|
||||||
|
height: "400px",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const getFields = function () {
|
||||||
|
fields = pagedata ? $copy(pagedata.fields) : [];
|
||||||
|
fields.map(
|
||||||
|
(v) => (v.caption = (v.label ? v.label.indexOf("<") >= 0 : false) ? v.name : v.label)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const doSelect = function (evt) {
|
||||||
|
emit("modalevent", { name: "selected", data: evt[props.field.name] });
|
||||||
|
};
|
||||||
|
const changeLabel = function (text) {
|
||||||
|
currentField.label = text;
|
||||||
|
updateFields(currentField);
|
||||||
|
};
|
||||||
|
function editLabel() {
|
||||||
|
showmodal.value = {
|
||||||
|
component: "datatable/EditLabel",
|
||||||
|
width: "500px",
|
||||||
|
height: "300px",
|
||||||
|
vbind: { label: label },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const changeTemplate = function (v) {
|
||||||
|
radioTemplate.value = v.code;
|
||||||
|
let copy = $copy(currentField);
|
||||||
|
copy.template = v.code;
|
||||||
|
updateFields(copy);
|
||||||
|
};
|
||||||
|
function createField() {
|
||||||
|
showmodal.value = {
|
||||||
|
component: "datatable/NewField",
|
||||||
|
vbind: { pagename: props.pagename },
|
||||||
|
title: "Tạo cột mới",
|
||||||
|
width: "50%",
|
||||||
|
height: "630px",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function copyContent(value) {
|
||||||
|
$copyToClipboard(value);
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
showmodal.value = undefined;
|
||||||
|
}
|
||||||
|
const updateFields = function (field, type) {
|
||||||
|
let copy = $clone(store[props.pagename]);
|
||||||
|
let idx = copy.fields.findIndex((v) => v.name === field.name);
|
||||||
|
copy.fields[idx] = field;
|
||||||
|
store.commit(props.pagename, copy);
|
||||||
|
};
|
||||||
|
const changeTab = function (v) {
|
||||||
|
currentTab.value = v.code;
|
||||||
|
selectTab = v;
|
||||||
|
};
|
||||||
|
const saveSetting = function () {
|
||||||
|
showmodal.value = {
|
||||||
|
component: "datatable/MenuSave",
|
||||||
|
vbind: { pagename: props.pagename, classify: 4 },
|
||||||
|
title: "Lưu thiết lập",
|
||||||
|
width: "500px",
|
||||||
|
height: "400px",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const showSidebar = function () {
|
||||||
|
let event = { name: "template", field: currentField };
|
||||||
|
let title = "Danh sách cột";
|
||||||
|
if (event.name === "bgcolor")
|
||||||
|
title = `Đổi màu nền: ${event.field.name} / ${$stripHtml(event.field.label, 30)}`;
|
||||||
|
else if (event.name === "color")
|
||||||
|
title = `Đổi màu chữ: ${event.field.name} / ${$stripHtml(event.field.label, 30)}`;
|
||||||
|
else if (event.name === "template")
|
||||||
|
title = `Định dạng nâng cao: ${$stripHtml(event.field.label, 30)}`;
|
||||||
|
showmodal.value = {
|
||||||
|
component: "datatable/FormatOption",
|
||||||
|
vbind: { event: event, currentField: currentField, pagename: props.pagename },
|
||||||
|
width: "850px",
|
||||||
|
height: "700px",
|
||||||
|
title: title,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
function resizeWidth(minus) {
|
||||||
|
let val = maxwidth || minwidth || 80;
|
||||||
|
val = minus ? parseInt(val - 0.1 * val) : parseInt(val + 0.1 * val);
|
||||||
|
if (val > 1000) return $snackbar("Độ rộng cột lớn hơn giới hạn cho phép");
|
||||||
|
else if (val < 20) return $snackbar("Độ rộng cột nhỏ hơn giới hạn cho phép");
|
||||||
|
radioMaxWidth = store.colorchoice.find((v) => v.code === "option");
|
||||||
|
radioWidth = store.colorchoice.find((v) => v.code === "option");
|
||||||
|
maxwidth = val;
|
||||||
|
currentField.maxwidth = val;
|
||||||
|
minwidth = val;
|
||||||
|
currentField.minwidth = val;
|
||||||
|
updateFields(currentField);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
431
components/datatable/CreateTemplate.vue
Normal file
431
components/datatable/CreateTemplate.vue
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="docid">
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Đối tượng</label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<b-radio v-for="(v,i) in types" :key="i" v-model="type"
|
||||||
|
:native-value="v" @input="changeType(v)">
|
||||||
|
{{v.name}}
|
||||||
|
</b-radio>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Kích cỡ</label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<b-radio v-for="(v,i) in sizes.filter(v=>type? (type.code==='tag'? v.code!=='is-small' : 1>0) : true)" :key="i" v-model="size"
|
||||||
|
:native-value="v" @input="changeType(v)">
|
||||||
|
{{v.name}}
|
||||||
|
</b-radio>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" v-if="['tag'].find(v=>v===type.code)">Hình khối</label>
|
||||||
|
<p class="control fs-14" v-if="['tag'].find(v=>v===type.code)">
|
||||||
|
<b-radio v-for="(v,i) in shapes" :key="i" v-model="shape"
|
||||||
|
:native-value="v" @input="changeType(v)">
|
||||||
|
{{v.name}}
|
||||||
|
</b-radio>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal" v-if="['tag'].find(v=>v===type.code)">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field" v-if="type.code!=='tag'">
|
||||||
|
<label class="label">Outline</label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<b-radio v-for="(v,i) in outlines" :key="i" v-model="outline"
|
||||||
|
:native-value="v" @input="changeType(v)">
|
||||||
|
{{v.name}}
|
||||||
|
</b-radio>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tags" v-if="type.code==='tag'">
|
||||||
|
<a :class="getClass(v)" v-for="(v,i) in colorscheme" :key="i"
|
||||||
|
@click="doSelect(v)" :ref="'tag' + i"> {{v.name}} </a>
|
||||||
|
</div>
|
||||||
|
<div class="pt-2" v-else-if="type.code==='span'">
|
||||||
|
<a class="mr-3" :class="getSpanClass(v)" v-for="(v,i) in colorscheme" :key="i"
|
||||||
|
@click="doSelectSpan(v)" :ref="'span' + i"> {{v.name}} </a>
|
||||||
|
</div>
|
||||||
|
<div :class="`tabs is-boxed mt-5 mb-5 ${tab.code==='template'? '' : 'pb-2'}`">
|
||||||
|
<ul>
|
||||||
|
<li :class="tab.code===v.code? 'is-active' : ''"
|
||||||
|
v-for="(v,i) in tabs" :key="i" @click="changeTab(v)"><a class="fs-15">{{v.name}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="tab.code==='selected'">
|
||||||
|
<a v-for="(v,i) in tags" :key="i" @click="selected=v">
|
||||||
|
<div class="field is-grouped is-grouped-multiline mt-4">
|
||||||
|
<p class="control">
|
||||||
|
<a :class="v.class">
|
||||||
|
{{v.name}}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<input class="input is-small" type="text" v-model="v.name">
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<a @click="remove(i)">
|
||||||
|
<SvgIcon v-bind="{name: 'close.svg', type: 'danger', size: 22}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="control has-text-right ml-5" v-if="selected? selected.id===v.id : false">
|
||||||
|
<SvgIcon v-bind="{name: 'tick.svg', type: 'primary', size: 22}"></SvgIcon>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="tab.code==='condition'">
|
||||||
|
<div class="mb-5" v-if="selected">
|
||||||
|
<b-radio v-for="(v,i) in conditions" :key="i" v-model="condition"
|
||||||
|
:native-value="v" @input="changeCondition(v)">
|
||||||
|
{{v.name}}
|
||||||
|
</b-radio>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="condition? condition.code==='yes' : false">
|
||||||
|
<div class="field mt-3">
|
||||||
|
<label class="label fs-14">Chọn trường xây dựng biểu thức <span class="has-text-danger"> * </span> </label>
|
||||||
|
<div class="control">
|
||||||
|
<b-taginput
|
||||||
|
size="is-small"
|
||||||
|
v-model="tagsField"
|
||||||
|
:data="pageData? pageData.fields.filter(v=>v.format==='number') : []"
|
||||||
|
type="is-dark is-light"
|
||||||
|
autocomplete
|
||||||
|
:open-on-focus="true"
|
||||||
|
field="name"
|
||||||
|
icon="plus"
|
||||||
|
placeholder="Chọn trường"
|
||||||
|
>
|
||||||
|
<template slot-scope="props">
|
||||||
|
<span class="mr-3 has-text-danger has-text-weight-bold"> {{props.option.name}}</span>
|
||||||
|
<span :class="tagsField.find(v=>v.id===props.option.id)? 'has-text-dark' : ''"> {{$stripHtml(props.option.label, 50)}} </span>
|
||||||
|
</template>
|
||||||
|
<template slot="empty">
|
||||||
|
Không có trường thỏa mãn
|
||||||
|
</template>
|
||||||
|
</b-taginput>
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='tagsField')"> {{errors.find(v=>v.name==='tagsField').message}} </p>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-1" v-if="tagsField.length>0">
|
||||||
|
<p class="help is-primary"> Click đúp vào để thêm vào biểu thức.</p>
|
||||||
|
<div class="tagsField">
|
||||||
|
<a @dblclick="expression = expression? (expression + ' ' + v.name) : v.name"
|
||||||
|
class="tag is-rounded" v-for="(v,i) in tagsField" :key="i">
|
||||||
|
<span class="tooltip">
|
||||||
|
{{v.name}}
|
||||||
|
<span class="tooltiptext">{{ $stripHtml(v.label) }}</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14">Biểu thức có dạng Đúng / Sai <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control is-expanded">
|
||||||
|
<input class="input" type="text" v-model="expression" placeholder="Tạo biểu thức tại đây">
|
||||||
|
</p>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='expression')"> {{errors.find(v=>v.name==='expression').message}} </p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="tab.code==='option' && selected">
|
||||||
|
<div class="field is-horizontal border-bottom pb-2 mt-1">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14">Màu nền </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<b-radio v-for="(v,i) in colorchoice.filter(v=>v.code!=='condition')" :key="i" v-model="radioBGcolor"
|
||||||
|
:native-value="v" @input="changeStyle()">
|
||||||
|
{{v.name}}
|
||||||
|
</b-radio>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field" v-if="radioBGcolor? radioBGcolor.code==='option' : false">
|
||||||
|
<label class="label fs-14"> Mã màu <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input type="color" v-model="bgcolor" @change="changeStyle()">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-horizontal border-bottom pb-2">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14">Màu chữ </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<b-radio v-for="(v,i) in colorchoice.filter(v=>v.code!=='condition')" :key="i" v-model="radioColor"
|
||||||
|
:native-value="v" @input="changeStyle()">
|
||||||
|
{{v.name}}
|
||||||
|
</b-radio>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field" v-if="radioColor? radioColor.code==='option' : false">
|
||||||
|
<label class="label fs-14"> Mã màu <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input type="color" v-model="color" @change="changeStyle()">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal border-bottom pb-2">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14">Cỡ chữ </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<b-radio v-for="(v,i) in colorchoice.filter(v=>v.code!=='condition')" :key="i" v-model="radioSize"
|
||||||
|
:native-value="v" @input="changeStyle()">
|
||||||
|
{{v.name}}
|
||||||
|
</b-radio>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field" v-if="radioSize? radioSize.code==='option' : false">
|
||||||
|
<label class="label fs-14"> Cỡ chữ <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input class="input is-small" type="text" placeholder="Nhập số" v-model="textsize" @change="changeStyle()">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="tab.code==='template'">
|
||||||
|
<p class="mb-3">
|
||||||
|
<a @click="copyContent()" class="mr-6">
|
||||||
|
<span class="icon-text">
|
||||||
|
<SvgIcon class="mr-2" v-bind="{name: 'copy.svg', type: 'primary', siz: 18}"></SvgIcon>
|
||||||
|
<span class="fs-16">Copy</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a @click="paste()" class="mr-6">
|
||||||
|
<span class="icon-text">
|
||||||
|
<SvgIcon class="mr-2" v-bind="{name: 'pen1.svg', type: 'primary', siz: 18}"></SvgIcon>
|
||||||
|
<span class="fs-16">Paste</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<textarea class="textarea fs-14" rows="8" v-model="text" @dblclick="doCheck"></textarea>
|
||||||
|
</div>
|
||||||
|
<p class="mt-5">
|
||||||
|
<span class="icon-text fsb-18">
|
||||||
|
Replace
|
||||||
|
<SvgIcon v-bind="{name: 'right.svg', type: 'dark', size: 22}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<div class="field is-grouped mt-4">
|
||||||
|
<div class="control">
|
||||||
|
<p class="fsb-14 mb-1">Đoạn text</p>
|
||||||
|
<input class="input" type="text" placeholder="" v-model="source">
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<p class="fsb-14 mb-1">Thay bằng</p>
|
||||||
|
<input class="input" type="text" placeholder="" v-model="target">
|
||||||
|
</div>
|
||||||
|
<div class="control pl-5">
|
||||||
|
<button class="button is-primary is-outlined mt-5" @click="replace()">Replace</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-5">
|
||||||
|
<button class="button is-primary has-text-white" @click="changeTemplate()">Áp dụng</button>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useStore } from '@/stores/index'
|
||||||
|
const store = useStore()
|
||||||
|
const { $id, $copy, $empty, $stripHtml, $calc, $remove, $copyToClipboard } = useNuxtApp()
|
||||||
|
var props = defineProps({
|
||||||
|
pagename: String,
|
||||||
|
field: Object
|
||||||
|
})
|
||||||
|
var colorscheme = store.colorscheme
|
||||||
|
var colorchoice = store.colorchoice
|
||||||
|
var pageData = store[props.pagename]
|
||||||
|
var field = props.field
|
||||||
|
var type = undefined
|
||||||
|
var size = undefined
|
||||||
|
var types = [{code: 'span', name: 'span'}, {code: 'tag', name: 'tag'}]
|
||||||
|
var sizes = [{code: 'is-small', name: 'Nhỏ', value: 'is-size-6'}, {code: 'is-normal', name: 'Trung bình', value: 'is-size-5'},
|
||||||
|
{code: 'is-medium', name: 'Lớn', value: 'is-size-4'}]
|
||||||
|
var shapes = [{code: 'default', name: 'Mặc định'}, {code: 'is-rounded', name: 'Tròn góc'}]
|
||||||
|
var shape = undefined
|
||||||
|
var outlines = [{code: 'default', name: 'Mặc định'}, {code: 'is-outlined', name: 'Outline'}]
|
||||||
|
var outline = undefined
|
||||||
|
var conditions = [{code: 'no', name: 'Không áp dụng'}, {code: 'yes', name: 'Có áp dụng'}]
|
||||||
|
var condition = undefined
|
||||||
|
var tags = []
|
||||||
|
var selected = undefined
|
||||||
|
var tabs = [{code: 'selected', name: 'Bước 1: Tạo nội dung'}, {code: 'condition', name: 'Bước 2: Đặt điều kiện'}, {code: 'option', name: 'Bước 3: Chọn màu, cỡ chữ'},
|
||||||
|
{code: 'template', name: 'Bước 4: Mã lệnh & áp dụng'}]
|
||||||
|
var tab = ref(undefined)
|
||||||
|
var tagsField = []
|
||||||
|
var errors = []
|
||||||
|
var expression = ''
|
||||||
|
var text = ref(null)
|
||||||
|
var radioBGcolor = undefined
|
||||||
|
var radioColor = undefined
|
||||||
|
var radioSize = undefined
|
||||||
|
var bgcolor = undefined
|
||||||
|
var color = undefined
|
||||||
|
var textsize = undefined
|
||||||
|
var source = undefined
|
||||||
|
var target = $copy(field.name)
|
||||||
|
|
||||||
|
const initData = function() {
|
||||||
|
type = types.find(v=>v.code==='tag')
|
||||||
|
size = sizes.find(v=>v.code==='is-normal')
|
||||||
|
shape = shapes.find(v=>v.code==='is-rounded')
|
||||||
|
outline = shapes.find(v=>v.code==='default')
|
||||||
|
if($empty(field.template)) tab.value =tabs.find(v=>v.code==='selected')
|
||||||
|
else {
|
||||||
|
text.value =$copy(field.template)
|
||||||
|
tab.value =tabs.find(v=>v.code==='template')
|
||||||
|
}
|
||||||
|
condition =conditions.find(v=>v.code==='no')
|
||||||
|
}
|
||||||
|
/*watch: {
|
||||||
|
expression: function(newVal) {
|
||||||
|
if($empty(newVal)) return
|
||||||
|
elsecheckExpression()
|
||||||
|
},
|
||||||
|
tab: function(newVal, oldVal) {
|
||||||
|
if(oldVal===undefined) return
|
||||||
|
if(newVal.code==='template') {
|
||||||
|
let value = '<div>'
|
||||||
|
tags.map((v,i)=>{
|
||||||
|
value += '<span class="' + v.class + (tags.length>i+1? ' mr-2' : '') + '" '
|
||||||
|
if(v.style) value += 'style="' + v.style + '" '
|
||||||
|
value += (v.expression? ' v-if="' + v.expression + '"' : '') + '>' + v.name + '</span>'
|
||||||
|
})
|
||||||
|
value += '</div>'
|
||||||
|
text = value
|
||||||
|
} else if(newVal.code==='option') {
|
||||||
|
if(!selected) return
|
||||||
|
radioBGcolor =selected.bgcolor?colorchoice.find(v=>v.code==='option') :colorchoice.find(v=>v.code==='none')
|
||||||
|
radioColor =selected.color?colorchoice.find(v=>v.code==='option') :colorchoice.find(v=>v.code==='none')
|
||||||
|
radioSize =selected.textsize?colorchoice.find(v=>v.code==='option') :colorchoice.find(v=>v.code==='none')
|
||||||
|
bgcolor =selected.bgcolor?selected.bgcolor : undefined
|
||||||
|
color =selected.color?selected.color : undefined
|
||||||
|
textsize =selected.textsize?selected.textsize : undefined
|
||||||
|
} else if(newVal.code==='condition') {
|
||||||
|
condition = conditions.find(v=>v.code==='no')
|
||||||
|
tagsField = []
|
||||||
|
expression = ''
|
||||||
|
if(selected?selected.expression : false) {
|
||||||
|
condition =conditions.find(v=>v.code==='yes')
|
||||||
|
tagsField =$copy(selected.tags)
|
||||||
|
expression =$copy(selected.formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},*/
|
||||||
|
function changeTab(v) {
|
||||||
|
tab.value = v
|
||||||
|
}
|
||||||
|
const paste = async function() {
|
||||||
|
text.value = await navigator.clipboard.readText()
|
||||||
|
}
|
||||||
|
const replace = function() {
|
||||||
|
if($empty(text.value)) return
|
||||||
|
text.value =text.value.replaceAll(source,target)
|
||||||
|
}
|
||||||
|
const doCheck = function() {
|
||||||
|
let text = window.getSelection().toString()
|
||||||
|
if($empty(text)) return
|
||||||
|
source = text
|
||||||
|
}
|
||||||
|
const changeStyle = function() {
|
||||||
|
selected.bgcolor =selected.color =selected.textsize =selected.style = undefined
|
||||||
|
let style = ''
|
||||||
|
if(radioBGcolor.code==='option'? !$empty(bgcolor) : false) {
|
||||||
|
selected.bgcolor =bgcolor
|
||||||
|
style += 'background-color: ' +bgcolor + ' !important; '
|
||||||
|
}
|
||||||
|
if(radioColor.code==='option'? !$empty(color) : false) {
|
||||||
|
selected.color =color
|
||||||
|
style += 'color: ' +color + ' !important; '
|
||||||
|
}
|
||||||
|
if(radioSize.code==='option'?$isNumber(textsize) : false) {
|
||||||
|
selected.textsize =textsize
|
||||||
|
style += 'font-size: ' +textsize + 'px !important; '
|
||||||
|
}
|
||||||
|
$empty(style)? false :selected.style = style
|
||||||
|
}
|
||||||
|
const changeCondition = function(v) {
|
||||||
|
if(v.code==='no')selected.expression = undefined
|
||||||
|
}
|
||||||
|
const copyContent = function() {
|
||||||
|
$copyToClipboard(text.value)
|
||||||
|
}
|
||||||
|
const changeTemplate = function() {
|
||||||
|
let copy = pageData
|
||||||
|
let found = copy.fields.find(v=>v.name===field.name)
|
||||||
|
found.template = text.value
|
||||||
|
store.commit(props.pagename, copy)
|
||||||
|
}
|
||||||
|
const checkExpression = function() {
|
||||||
|
errors = []
|
||||||
|
let val =$copy(expression)
|
||||||
|
let exp =$copy(expression)
|
||||||
|
tagsField.forEach(v => {
|
||||||
|
let myRegExp = new RegExp(v.name, 'g')
|
||||||
|
val = val.replace(myRegExp, Math.random())
|
||||||
|
exp = exp.replace(myRegExp, "formatNumber(row['" + v.name + "'])")
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
let value =$calc(val)
|
||||||
|
if(isNaN(value) || value===Number.POSITIVE_INFINITY || value===Number.NEGATIVE_INFINITY) {
|
||||||
|
errors.push({name: 'expression', message: 'Biểu thức không hợp lệ'})
|
||||||
|
} else if(!(eval(value)===true || eval(value)===false)) {
|
||||||
|
errors.push({name: 'expression', message: 'Biểu thức không hợp lệ'})
|
||||||
|
} else if(selected) {
|
||||||
|
selected.expression = exp
|
||||||
|
selected.formula =expression
|
||||||
|
selected.tags =$copy(tagsField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
errors.push({name: 'expression', message: 'Biểu thức không hợp lệ'})
|
||||||
|
}
|
||||||
|
returnerrors.length>0? false : true
|
||||||
|
}
|
||||||
|
const changeType = function(v) {
|
||||||
|
}
|
||||||
|
const doSelect = function(v) {
|
||||||
|
tags.push({id:$id(), name: v.name, class:getClass(v)})
|
||||||
|
tab =tabs.find(v=>v.code==='selected')
|
||||||
|
selected =tags[tags.length-1]
|
||||||
|
}
|
||||||
|
const doSelectSpan = function(v) {
|
||||||
|
tags.push({id:$id(), name: v.name, class:getSpanClass(v)})
|
||||||
|
tab =tabs.find(v=>v.code==='selected')
|
||||||
|
selected =tags[tags.length-1]
|
||||||
|
}
|
||||||
|
const remove = function(i) {
|
||||||
|
$remove(tags, i)
|
||||||
|
}
|
||||||
|
const getClass = function(v) {
|
||||||
|
let value =type.code + ' ' + v.code + ' ' +size.code + (shape.code==='default'? '' : ' ' +shape.code)
|
||||||
|
value += (outline.code==='default'? '' : ' ' +outline.code)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
const getSpanClass = function(v) {
|
||||||
|
let value = 'has-text-' + v.name.toLowerCase() + ' ' +size.value
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
initData()
|
||||||
|
var docid = $id()
|
||||||
|
</script>
|
||||||
193
components/datatable/DataModel.vue
Normal file
193
components/datatable/DataModel.vue
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
<template>
|
||||||
|
<div class="columns mx-0">
|
||||||
|
<div class="column is-2">
|
||||||
|
<Caption class="mb-2" v-bind="{title: 'Tên model (bảng)', type: 'has-text-warning'}"></Caption>
|
||||||
|
<div class="mb-2">
|
||||||
|
<input class="input" v-model="text" placeholder="Tìm model" @change="findModel()">
|
||||||
|
</div>
|
||||||
|
<div style="max-height: 80vh; overflow: auto;">
|
||||||
|
<div :class="`py-1 border-bottom is-clickable ${current.model===v.model? 'has-background-primary has-text-white' : ''}`"
|
||||||
|
v-for="v in displayData" @click="changeMenu(v)">
|
||||||
|
{{ v.model}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-10 py-0 px-0">
|
||||||
|
<div class="tabs mb-3">
|
||||||
|
<ul>
|
||||||
|
<li :class="`${v.code===tab? 'is-active has-text-weight-bold fs-18' : 'fs-18'}`" v-for="v in tabs">
|
||||||
|
<a @click="changeTab(v)">{{v.name}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-if="tab==='datatype'">
|
||||||
|
<Caption class="mb-2" v-bind="{title: 'Kiểu dữ liệu (type)', type: 'has-text-warning'}"></Caption>
|
||||||
|
<div style="max-height:75vh; overflow-y: auto;">
|
||||||
|
<div class="py-1 border-bottom is-clickable" v-for="x in current.fields">
|
||||||
|
{{ x.name}}
|
||||||
|
<span class="ml-6 has-text-grey">{{ x.type }}</span>
|
||||||
|
<a class="ml-6 has-text-primary" v-if="x.model" @click="openModel(x)">{{ x.model }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template v-else-if="tab==='table'">
|
||||||
|
<div class="columns mx-0 mb-0 pb-0">
|
||||||
|
<div class="column is-5">
|
||||||
|
<Caption class="mb-1" v-bind="{title: 'Values', type: 'has-text-warning'}"></Caption>
|
||||||
|
<input class="input" rows="1" v-model="values" placeholder="Tên trường không chứa dấu cách, vd: code,name">
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<Caption class="mb-1" v-bind="{title: 'Filter', type: 'has-text-warning'}"></Caption>
|
||||||
|
<input class="input" rows="1" v-model="filter" placeholder="{'code': 'xyz'}">
|
||||||
|
</div>
|
||||||
|
<div class="column is-2">
|
||||||
|
<Caption class="mb-1" v-bind="{title: 'Sort', type: 'has-text-warning'}"></Caption>
|
||||||
|
<input class="input" rows="1" v-model="sort" placeholder="vd: -code,name">
|
||||||
|
</div>
|
||||||
|
<div class="column is-1">
|
||||||
|
<Caption class="mb-1" v-bind="{title: 'Load', type: 'has-text-warning'}"></Caption>
|
||||||
|
<div>
|
||||||
|
<button class="button is-primary has-text-white" @click="loadData()">Load</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Caption class="mb-1" v-bind="{title: 'Query', type: 'has-text-warning'}"></Caption>
|
||||||
|
<div class="mb-2">
|
||||||
|
{{ query }}
|
||||||
|
<a class="has-text-primary ml-5" @click="copy()">copy</a>
|
||||||
|
<p>{{apiUrl}}
|
||||||
|
<a class="has-text-primary ml-5" @click="$copyToClipboard(apiUrl)">copy</a>
|
||||||
|
<a class="has-text-primary ml-5" target="_blank" :href="apiUrl">open</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<DataTable v-bind="{pagename: pagename}" v-if="pagedata" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-else>
|
||||||
|
<img id="image" :src="filePath" alt="">
|
||||||
|
<p class="pl-5">
|
||||||
|
<a class="mr-5" @click="downloadFile()">
|
||||||
|
<SvgIcon v-bind="{name: 'download.svg', type: 'black', size: 24}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a target="_blank" :href="filePath">
|
||||||
|
<SvgIcon v-bind="{name: 'open.svg', type: 'black', size: 24}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { useStore } from '@/stores/index'
|
||||||
|
const { $getdata, $getapi, $createField, $clone, $getpage, $empty, $copyToClipboard, $find, $multiSort, $download, $getpath } = useNuxtApp()
|
||||||
|
const store = useStore()
|
||||||
|
var pagename = 'pagedata3'
|
||||||
|
var pagedata = ref()
|
||||||
|
pagedata.value = $getpage()
|
||||||
|
pagedata.value.perPage = 10
|
||||||
|
store.commit(pagename, pagedata)
|
||||||
|
let list = ['LogEntry', 'Permission', 'ContentType', 'Session', 'Group']
|
||||||
|
var data = (await $getdata('getmodel')).filter(v=>list.findIndex(x=>x===v.model)<0)
|
||||||
|
data = $multiSort(data, {model: 'asc'})
|
||||||
|
var current = ref({fields: []})
|
||||||
|
var tabs = [{code: 'datatype', name: 'Kiểu dữ liệu'}, {code: 'table', name: 'Dữ liệu'}, {code: 'datamodel', name: 'Data model'}]
|
||||||
|
var tab = ref('datatype')
|
||||||
|
var datatable = ref()
|
||||||
|
var query = ref()
|
||||||
|
var values, filter
|
||||||
|
var apiUrl = ref()
|
||||||
|
var showmodal = ref()
|
||||||
|
var text = null
|
||||||
|
var displayData = ref(data)
|
||||||
|
var filePath = `${$getpath()}static/files/datamodel.png`
|
||||||
|
var sort = "-id"
|
||||||
|
current.value = data[0]
|
||||||
|
function changeMenu(v) {
|
||||||
|
values = undefined
|
||||||
|
filter = undefined
|
||||||
|
sort = undefined
|
||||||
|
current.value = v
|
||||||
|
if(tab.value==='table') loadData()
|
||||||
|
}
|
||||||
|
async function changeTab(v) {
|
||||||
|
tab.value = v.code
|
||||||
|
if(v.code==='table') loadData()
|
||||||
|
}
|
||||||
|
async function loadData() {
|
||||||
|
let vfilter = filter? filter.trim() : undefined
|
||||||
|
if(vfilter) {
|
||||||
|
try {
|
||||||
|
vfilter = JSON.parse(vfilter)
|
||||||
|
} catch (error) {
|
||||||
|
alert('Cấu trúc filter có lỗi')
|
||||||
|
vfilter = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let params = {values: $empty(values)? undefined : values.trim(), filter: filter, sort: $empty(sort)? undefined : sort.trim()}
|
||||||
|
let modelName = current.value.model
|
||||||
|
let found = {name: modelName.toLowerCase().replace('_', ''), url: `data/${modelName}/`, url_detail: `data-detail/${modelName}/`, params: params}
|
||||||
|
query.value = $clone(found)
|
||||||
|
let rs = await $getapi([found])
|
||||||
|
if(rs==='error') return alert('Đã xảy ra lỗi, hãy xem lại câu lệnh.')
|
||||||
|
datatable.value = rs[0].data.rows
|
||||||
|
showData()
|
||||||
|
|
||||||
|
// api query
|
||||||
|
const baseUrl = $getpath() + `${query.value.url}`
|
||||||
|
apiUrl.value = baseUrl
|
||||||
|
let vparams = !$empty(values)? {values: values} : null
|
||||||
|
if(!$empty(filter)) {
|
||||||
|
vparams = vparams? {values: values, filter:filter} : {filter:filter}
|
||||||
|
}
|
||||||
|
if(!$empty(sort)) {
|
||||||
|
if(vparams) {
|
||||||
|
vparams.sort = sort.trim()
|
||||||
|
} else {
|
||||||
|
vparams = {sort: sort.trim()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(vparams) {
|
||||||
|
let url = new URL(baseUrl);
|
||||||
|
let searchParams = new URLSearchParams(vparams);
|
||||||
|
url.search = searchParams.toString();
|
||||||
|
apiUrl.value = baseUrl + url.search
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showData() {
|
||||||
|
let arr = []
|
||||||
|
if(!$empty(values)) {
|
||||||
|
let arr1 = values.trim().split(',')
|
||||||
|
arr1.map(v=>{
|
||||||
|
let val = v.trim()
|
||||||
|
let field = $createField(val, val, 'string', true)
|
||||||
|
arr.push(field)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
current.value.fields.map(v=>{
|
||||||
|
let field = $createField(v.name, v.name, 'string', true)
|
||||||
|
arr.push(field)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let clone = $clone(pagedata.value)
|
||||||
|
clone.fields = arr
|
||||||
|
clone.data = datatable.value
|
||||||
|
pagedata.value = undefined
|
||||||
|
setTimeout(()=>pagedata.value = clone)
|
||||||
|
}
|
||||||
|
function copy() {
|
||||||
|
$copyToClipboard(JSON.stringify(query.value))
|
||||||
|
}
|
||||||
|
function openModel(x) {
|
||||||
|
showmodal.value = {component: 'datatable/ModelInfo', title: x.model, width: '70%', height: '600px',
|
||||||
|
vbind: {data: data, info: $find(data, {model: x.model})}}
|
||||||
|
}
|
||||||
|
function downloadFile() {
|
||||||
|
$download(`${$getpath()}download/?name=datamodel.png&type=file`, 'datamodel.png')
|
||||||
|
}
|
||||||
|
function findModel() {
|
||||||
|
if($empty(text)) return displayData.value = data
|
||||||
|
displayData.value = data.filter(v=>v.model.toLowerCase().indexOf(text.toLowerCase())>=0)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
422
components/datatable/DataTable.vue
Normal file
422
components/datatable/DataTable.vue
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
<template>
|
||||||
|
<div class="field is-grouped is-grouped-multiline pl-2" v-if="filters? filters.length>0 : false">
|
||||||
|
<div class="control mr-5">
|
||||||
|
<a class="button is-primary is-small has-text-white has-text-weight-bold" @click="updateData({filters: []})">
|
||||||
|
<span class="fs-14">Xóa lọc</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="control pr-2 mr-5">
|
||||||
|
<span class="icon-text">
|
||||||
|
<SvgIcon v-bind="{name: 'sigma.svg', type: 'primary', size: 20}"></SvgIcon>
|
||||||
|
<span class="fsb-18 has-text-primary">{{totalRows}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="control" v-for="(v,i) in filters" :key="i">
|
||||||
|
<div class="tags has-addons is-marginless">
|
||||||
|
<a class="tag is-primary has-text-white is-marginless" @click="showCondition(v)">{{v.label.indexOf('>')>=0? $stripHtml(v.label,30) : v.label}}</a>
|
||||||
|
<a class="tag is-delete is-marginless has-text-black-bis" @click="removeFilter(i)"></a>
|
||||||
|
</div>
|
||||||
|
<span class="help has-text-black-bis">
|
||||||
|
{{v.sort? v.sort : (v.select? ('[' + (v.select.length>0? $stripHtml(v.select[0],20) : '') + '...Σ' + v.select.length + ']') :
|
||||||
|
(v.condition))}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-container mb-0" ref="container" id="docid">
|
||||||
|
<table class="table is-fullwidth is-bordered is-narrow is-hoverable" :style="tableStyle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th v-for="(field,i) in displayFields" :key="i" :style="field.headerStyle">
|
||||||
|
<div @click="showField(field)" :style="field.dropStyle">
|
||||||
|
<a v-if="field.label.indexOf('<')<0">{{field.label}}</a>
|
||||||
|
<a v-else>
|
||||||
|
<component :is="dynamicComponent(field.label)" :row="v" @clickevent="clickEvent($event, v, field)" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(v,i) in displayData" :key="i">
|
||||||
|
<td v-for="(field, j) in displayFields" :key="j" :id="`${field.name}`" :style="v[`${field.name}color`]"
|
||||||
|
@dblclick="doubleClick(field, v)">
|
||||||
|
<component :is="dynamicComponent(field.template)" :row="v" v-if="field.template" @clickevent="clickEvent($event, v, field)" />
|
||||||
|
<span v-else>{{ v[field.name] }}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<DatatablePagination v-bind="{data: data, perPage: perPage}" @changepage="changePage" v-if="showPaging"></DatatablePagination>
|
||||||
|
</div>
|
||||||
|
<Modal @close="close" @selected="doSelect" @confirm="confirmRemove" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { createApp } from "vue/dist/vue.esm-bundler.js"
|
||||||
|
import { ref, defineComponent } from 'vue'
|
||||||
|
import { useStore } from '~/stores/index'
|
||||||
|
const emit = defineEmits(['edit', 'insert', 'dataevent'])
|
||||||
|
const { $copy, $empty, $unique, $multiSort, $remove, $calc, $calculate, $find, $formatNumber, $stripHtml, $calculateData, $deleterow } = useNuxtApp()
|
||||||
|
const store = useStore()
|
||||||
|
var props = defineProps({
|
||||||
|
pagename: String
|
||||||
|
})
|
||||||
|
function dynamicComponent(htmlString) {
|
||||||
|
return defineComponent({
|
||||||
|
template: htmlString,
|
||||||
|
props: {
|
||||||
|
row: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
var timer = undefined
|
||||||
|
var showPaging = ref(false)
|
||||||
|
var totalRows = ref(0)
|
||||||
|
var currentPage = 1
|
||||||
|
var displayFields = ref([])
|
||||||
|
var displayData = []
|
||||||
|
var pagedata = store[props.pagename]
|
||||||
|
var tablesetting = $copy(pagedata.tablesetting || store.tablesetting)
|
||||||
|
var perPage = Number($find(tablesetting, {code: 'per-page'}, 'detail')) || 20
|
||||||
|
var filters = $copy(pagedata.filters || [])
|
||||||
|
var currentField
|
||||||
|
var filterData = []
|
||||||
|
var currentsetting
|
||||||
|
var scrollbar
|
||||||
|
var fields
|
||||||
|
var currentRow
|
||||||
|
var data = $copy(pagedata.data)
|
||||||
|
var showmodal = ref()
|
||||||
|
watch(() => store[props.pagename], (newVal, oldVal) => {
|
||||||
|
updateChange()
|
||||||
|
})
|
||||||
|
function updateChange() {
|
||||||
|
pagedata = store[props.pagename]
|
||||||
|
if(!pagedata.update) return
|
||||||
|
if(pagedata.update.data) data = $copy(pagedata.update.data)
|
||||||
|
if(pagedata.update.filters) {
|
||||||
|
doFilter(pagedata.update.filters)
|
||||||
|
updateShow()
|
||||||
|
return //exit
|
||||||
|
}
|
||||||
|
if(filters.length>0) doFilter(filters)
|
||||||
|
if(pagedata.update.fields || pagedata.update.data) updateShow()
|
||||||
|
}
|
||||||
|
const updateShow = function(full_data) {
|
||||||
|
let arr = pagedata.fields.filter(v=>v.show)
|
||||||
|
if(full_data===false) displayData = $copy(data)
|
||||||
|
else displayData = $copy(data.filter((ele,index) => (index>=(currentPage-1)*perPage && index<currentPage*perPage)))
|
||||||
|
displayData.map(v=>{
|
||||||
|
arr.map(x=>v[`${x.name}color`] = getStyle(x, v))
|
||||||
|
})
|
||||||
|
arr.map(v=>{
|
||||||
|
v.headerStyle = getSettingStyle('header', v)
|
||||||
|
v.dropStyle = getSettingStyle('dropdown', v)
|
||||||
|
})
|
||||||
|
displayFields.value = arr
|
||||||
|
showPagination()
|
||||||
|
}
|
||||||
|
function confirmRemove() {
|
||||||
|
$deleterow(pagedata.api.name, currentRow.id, props.pagename, true)
|
||||||
|
}
|
||||||
|
const clickEvent = function(event, row, field) {
|
||||||
|
let name = typeof event === "string"? event : event.name
|
||||||
|
let data = typeof event === "string"? event : event.data
|
||||||
|
if(name==='remove') {
|
||||||
|
currentRow = row
|
||||||
|
showmodal.value = {component: `dialog/Confirm`,vbind: {content: 'Bạn có muốn xóa bản ghi này không?', duration: 10},
|
||||||
|
title: 'Xác nhận', width: '500px', height: '100px'}
|
||||||
|
}
|
||||||
|
emit(name, row, field, data)
|
||||||
|
}
|
||||||
|
const showField = async function(field) {
|
||||||
|
if(pagedata.contextMenu===false || field.menu==='no') return
|
||||||
|
currentField = field
|
||||||
|
filterData = $unique(pagedata.data, [field.name])
|
||||||
|
//let doc = this.$refs[`th${field.name}`]
|
||||||
|
//let width = (doc? doc.length>0 : false)? doc[0].getBoundingClientRect().width : 100
|
||||||
|
let width = 100
|
||||||
|
if(pagedata.setting) currentsetting = $copy(pagedata.setting)
|
||||||
|
showmodal.value = {vbind: {pagename: props.pagename, field: field, filters: filters, filterData: filterData, width: width},
|
||||||
|
component: 'datatable/ContextMenu', title: field.name, width: '650px', height: '500px'} //$stripHtml(field.label)
|
||||||
|
}
|
||||||
|
const getStyle = function(field, record) {
|
||||||
|
var stop = false
|
||||||
|
let val = tablesetting.find(v=>v.code==='td-border')? tablesetting.find(v=>v.code==='td-border').detail
|
||||||
|
: 'border: solid 1px rgb(44, 44, 44); '
|
||||||
|
val = val.indexOf(';')>=0? val : val + ';'
|
||||||
|
if(field.bgcolor? !Array.isArray(field.bgcolor) : false) {
|
||||||
|
val += ` background-color:${field.bgcolor}; `
|
||||||
|
} else if(field.bgcolor? Array.isArray(field.bgcolor) : false) {
|
||||||
|
field.bgcolor.map(v=>{
|
||||||
|
if(v.type==='search') {
|
||||||
|
if(record[field.name] && !stop? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase())>=0 : false) {
|
||||||
|
val += ` background-color:${v.color}; `
|
||||||
|
stop = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let res = $calculate(record, v.tags, v.expression)
|
||||||
|
if(res.success && res.value && !stop) {
|
||||||
|
val += ` background-color:${v.color}; `
|
||||||
|
stop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
stop = false
|
||||||
|
if(field.color? !Array.isArray(field.color) : false) {
|
||||||
|
val += ` color:${field.color}; `
|
||||||
|
} else if(field.color? Array.isArray(field.color) : false) {
|
||||||
|
field.color.map(v=>{
|
||||||
|
if(v.type==='search') {
|
||||||
|
if(record[field.name] && !stop? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase())>=0 : false) {
|
||||||
|
val += ` color:${v.color}; `
|
||||||
|
stop = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let res = $calculate(record, v.tags, v.expression)
|
||||||
|
if(res.success && res.value && !stop) {
|
||||||
|
val += ` color:${v.color}; `
|
||||||
|
stop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
stop = false
|
||||||
|
if(field.textsize? !Array.isArray(field.textsize) : false) {
|
||||||
|
val += ` font-size:${field.textsize}px; `
|
||||||
|
} else if(field.textsize? Array.isArray(field.textsize) : false) {
|
||||||
|
field.textsize.map(v=>{
|
||||||
|
if(v.type==='search') {
|
||||||
|
if(record[field.name] && !stop? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase())>=0 : false) {
|
||||||
|
val += ` font-size:${v.size}px; `
|
||||||
|
stop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let res = $calculate(record, v.tags, v.expression)
|
||||||
|
if(res.success && res.value && !stop) {
|
||||||
|
val += ` font-size:${v.size}px; `
|
||||||
|
stop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else val += ` font-size:${tablesetting.find(v=>v.code==='table-font-size').detail}px;`
|
||||||
|
if(field.textalign) val += ` text-align:${field.textalign}; `
|
||||||
|
if(field.minwidth) val += ` min-width:${field.minwidth}px; `
|
||||||
|
if(field.maxwidth) val += ` max-width:${field.maxwidth}px; `
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
const getSettingStyle = function(name, field) {
|
||||||
|
let value = ''
|
||||||
|
if(name==='container') {
|
||||||
|
value = 'min-height:' + tablesetting.find(v=>v.code==='container-height').detail + 'rem; '
|
||||||
|
} else if(name==='table') {
|
||||||
|
value += 'background-color:' + tablesetting.find(v=>v.code==='table-background').detail + '; '
|
||||||
|
value += 'font-size:' + tablesetting.find(v=>v.code==='table-font-size').detail + 'px;'
|
||||||
|
value += 'color:' + tablesetting.find(v=>v.code==='table-font-color').detail + '; '
|
||||||
|
} else if(name==='header') {
|
||||||
|
value += 'background-color:' + tablesetting.find(v=>v.code==='header-background').detail + '; '
|
||||||
|
if(field.minwidth) value += ' min-width: ' + field.minwidth + 'px; '
|
||||||
|
if(field.maxwidth) value += ' max-width: ' + field.maxwidth + 'px; '
|
||||||
|
} else if(name==='menu') {
|
||||||
|
let arg = tablesetting.find(v=>v.code==='menu-width').detail
|
||||||
|
arg = field? (field.menuwidth? field.menuwidth : arg) : arg
|
||||||
|
value += 'width:' + arg + 'rem; '
|
||||||
|
value += 'min-height:' + tablesetting.find(v=>v.code==='menu-min-height').detail + 'rem; '
|
||||||
|
value += 'max-height:' + tablesetting.find(v=>v.code==='menu-max-height').detail + 'rem; '
|
||||||
|
value += "overflow:auto; "
|
||||||
|
} else if(name==='dropdown') {
|
||||||
|
value += 'font-size:' + tablesetting.find(v=>v.code==='header-font-size').detail + 'px; '
|
||||||
|
let found = filters.find(v=>v.name===field.name)
|
||||||
|
found? value += 'color:' + tablesetting.find(v=>v.code==='header-filter-color').detail + '; '
|
||||||
|
:value += 'color:' + tablesetting.find(v=>v.code==='header-font-color').detail + '; '
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
function changePage(page) {
|
||||||
|
currentPage = page
|
||||||
|
updateShow()
|
||||||
|
}
|
||||||
|
const showPagination = function() {
|
||||||
|
showPaging.value = pagedata.pagination===false? false : true
|
||||||
|
totalRows.value = data.length
|
||||||
|
if(showPaging.value && pagedata.api) {
|
||||||
|
if(pagedata.api.full_data===false) totalRows.value = pagedata.api.total_rows
|
||||||
|
showPaging.value = totalRows.value > perPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const close = function() {
|
||||||
|
showmodal.value = undefined
|
||||||
|
}
|
||||||
|
const frontendFilter = function(newVal) {
|
||||||
|
let checkValid = function(name, x, filter) {
|
||||||
|
if($empty(x[name])) return false
|
||||||
|
else {
|
||||||
|
let text = ''
|
||||||
|
filter.map((y,k)=>{
|
||||||
|
text += `${k>0? (filter[k-1].operator==='and'? ' &&' : ' ||') : ''} ${$formatNumber(x[name])}
|
||||||
|
${y.condition==='='? '==' : (y.condition==='<>'? '!==' : y.condition)} ${$formatNumber(y.value)}`
|
||||||
|
})
|
||||||
|
return $calc(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newVal = $copy(newVal)
|
||||||
|
var data = $copy(pagedata.data)
|
||||||
|
newVal.filter(m=>m.select || m.filter).map(v => {
|
||||||
|
if(v.select) {
|
||||||
|
data = data.filter(x => v.select.findIndex(y => $empty(y)? $empty(x[v.name]) : (y===x[v.name])) >-1)
|
||||||
|
} else if(v.filter) {
|
||||||
|
data = data.filter(x => checkValid(v.name, x, v.filter))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let sort = {}
|
||||||
|
let format = {}
|
||||||
|
let list = filters.filter(x=>x.sort)
|
||||||
|
list.map(v=>{
|
||||||
|
sort[v.name] = v.sort === "az" ? "asc" : "desc"
|
||||||
|
format[v.name] = v.format;
|
||||||
|
})
|
||||||
|
return list.length>0? $multiSort(data, sort, format) : data
|
||||||
|
}
|
||||||
|
const backendFilter = function(newVal) {
|
||||||
|
|
||||||
|
}
|
||||||
|
const doFilter = function(newVal, nonset) {
|
||||||
|
if(currentPage>1 && nonset!==true) currentPage = 1
|
||||||
|
if(pagedata.api.full_data) {
|
||||||
|
data = frontendFilter(newVal)
|
||||||
|
pagedata.dataFilter = $copy(data)
|
||||||
|
store.commit(props.pagename, pagedata)
|
||||||
|
emit('changedata', newVal)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(timer) clearTimeout(timer)
|
||||||
|
timer = setTimeout(() => backendFilter(newVal), 200)
|
||||||
|
}
|
||||||
|
pagedata.filters = newVal
|
||||||
|
store.commit(props.pagename, pagedata)
|
||||||
|
emit('changefilter', newVal? newVal.length>0 : false)
|
||||||
|
}
|
||||||
|
const doSelect = function(value) {
|
||||||
|
showmodal.value = undefined
|
||||||
|
let field = currentField
|
||||||
|
let found = filters.find(v=>v.name===field.name)
|
||||||
|
if(found) {
|
||||||
|
!found.select? found.select = [] : false
|
||||||
|
let idx = found.select.findIndex(x=>x===value)
|
||||||
|
idx>=0? $remove(found.select, idx) : found.select.push(value)
|
||||||
|
if(found.select.length===0) {
|
||||||
|
idx = filters.findIndex(v=>v.name===field.name)
|
||||||
|
if(idx>=0) $remove(filters, idx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filters.push({name: field.name, label: field.label, select: [value], format: field.format})
|
||||||
|
}
|
||||||
|
doFilter(filters)
|
||||||
|
updateShow()
|
||||||
|
}
|
||||||
|
const doubleScroll = function(element) {
|
||||||
|
var _scrollbar= document.createElement('div');
|
||||||
|
_scrollbar.appendChild(document.createElement('div'));
|
||||||
|
_scrollbar.style.overflow= 'auto';
|
||||||
|
_scrollbar.style.overflowY= 'hidden';
|
||||||
|
_scrollbar.firstChild.style.width= element.scrollWidth+'px';
|
||||||
|
_scrollbar.firstChild.style.height = '1px'
|
||||||
|
_scrollbar.firstChild.appendChild(document.createTextNode('\xA0'));
|
||||||
|
var running = false;
|
||||||
|
_scrollbar.onscroll= function() {
|
||||||
|
if(running) {
|
||||||
|
running = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
running = true;
|
||||||
|
element.scrollLeft= _scrollbar.scrollLeft;
|
||||||
|
};
|
||||||
|
element.onscroll= function() {
|
||||||
|
if(running) {
|
||||||
|
running = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
running = true;
|
||||||
|
_scrollbar.scrollLeft= element.scrollLeft;
|
||||||
|
}
|
||||||
|
element.parentNode.insertBefore(scrollbar, element)
|
||||||
|
_scrollbar.scrollLeft= element.scrollLeft
|
||||||
|
scrollbar = _scrollbar
|
||||||
|
}
|
||||||
|
const removeFilter = function(i) {
|
||||||
|
$remove(filters, i)
|
||||||
|
doFilter(filters)
|
||||||
|
updateShow()
|
||||||
|
}
|
||||||
|
const scrollbarVisible = function() {
|
||||||
|
let element = this.$refs['container']
|
||||||
|
if(!element) return
|
||||||
|
let result = element.scrollWidth > element.clientWidth? true : false
|
||||||
|
if(scrollbar) {
|
||||||
|
element.parentNode.removeChild(scrollbar)
|
||||||
|
scrollbar = undefined
|
||||||
|
}
|
||||||
|
if(result) doubleScroll(element)
|
||||||
|
}
|
||||||
|
const updateData = async function(newVal) {
|
||||||
|
if(newVal.columns) { //change attribute
|
||||||
|
fields = $copy(newVal.columns)
|
||||||
|
let _fields = fields.filter(v=>v.show)
|
||||||
|
data.map(v=>{
|
||||||
|
_fields.map(x=>v[`${x.name}color`] = getStyle(x, v))
|
||||||
|
})
|
||||||
|
return updateShow()
|
||||||
|
}
|
||||||
|
if(newVal.tablesetting) {
|
||||||
|
tablesetting = newVal.tablesetting
|
||||||
|
perPage = $formatNumber(tablesetting.find(v=>v.code=="per-page").detail)
|
||||||
|
currentPage = 1
|
||||||
|
}
|
||||||
|
tablesetting = $copy(pagedata.tablesetting || gridsetting)
|
||||||
|
if(tablesetting) {
|
||||||
|
perPage = pagedata.perPage? pagedata.perPage : Number(tablesetting.find(v=>v.code==='per-page').detail)
|
||||||
|
}
|
||||||
|
if(newVal.fields) {
|
||||||
|
fields = $copy(newVal.fields)
|
||||||
|
} else fields = $copy(pagedata.fields)
|
||||||
|
if(newVal.data || newVal.fields) {
|
||||||
|
let copy = $copy(newVal.data || data)
|
||||||
|
this.data = $calculateData(copy, fields)
|
||||||
|
let fields = fields.filter(v=>v.show)
|
||||||
|
data.map(v=>{
|
||||||
|
fields.map(x=>v[`${x.name}color`] = getStyle(x, v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if(newVal.filters) filters = $copy(newVal.filters)
|
||||||
|
else if(pagedata.filters) filters = $copy(pagedata.filters)
|
||||||
|
if(newVal.data || newVal.fields || newVal.filters) {
|
||||||
|
let copy = $copy(filters)
|
||||||
|
filters.map((v,i)=>{
|
||||||
|
let idx = $findIndex(fields, {name: v.name})
|
||||||
|
let index = $findIndex(copy, {name: v.name})
|
||||||
|
if(idx<0 && index>=0) $delete(copy, index)
|
||||||
|
else if(idx>=0 && index>=0) copy[index].label = fields[idx].label
|
||||||
|
})
|
||||||
|
filters = copy
|
||||||
|
doFilter(filters)
|
||||||
|
}
|
||||||
|
if(newVal.data || newVal.fields || newVal.filters || newVal.tablesetting) updateShow()
|
||||||
|
if(newVal.data || newVal.fields) setTimeout(()=> scrollbarVisible(), 100)
|
||||||
|
if(newVal.highlight) setTimeout(()=>highlight(newVal.highlight), 50)
|
||||||
|
}
|
||||||
|
const doubleClick = function(field, v) {
|
||||||
|
currentField = field
|
||||||
|
doSelect(v[field.name])
|
||||||
|
}
|
||||||
|
var tableStyle = getSettingStyle('table')
|
||||||
|
setTimeout(()=> updateShow(), 200)
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
:deep(.table tbody tr:hover td, .table tbody tr:hover th) {
|
||||||
|
background-color: hsl(0, 0%, 78%);
|
||||||
|
color: rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
402
components/datatable/DataView.vue
Normal file
402
components/datatable/DataView.vue
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
<template>
|
||||||
|
<TimeOption
|
||||||
|
v-bind="{ pagename: vpagename, api: api, timeopt: timeopt, filter: optfilter, importdata: props.importdata, newDataAvailable: newDataAvailable, params: vparams }"
|
||||||
|
ref="timeopt" @option="timeOption" @excel="exportExcel" @add="insert" @manual-refresh="manualRefresh" @refresh-data="refreshData"
|
||||||
|
@import="openImportModal" class="mb-3" v-if="timeopt"></TimeOption>
|
||||||
|
<DataTable v-bind="{ pagename: vpagename }" @edit="edit" @insert="insert" @dataevent="dataEvent" v-if="pagedata" />
|
||||||
|
<Modal @close="showmodal = undefined" v-bind="showmodal" v-if="showmodal" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import TimeOption from '~/components/datatable/TimeOption'
|
||||||
|
import { useStore } from '~/stores/index'
|
||||||
|
import { ref, watch, onBeforeUnmount } from 'vue'
|
||||||
|
|
||||||
|
const emit = defineEmits(['modalevent', 'dataevent', 'dataUpdated'])
|
||||||
|
const store = useStore()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
pagename: String,
|
||||||
|
api: String,
|
||||||
|
setting: String,
|
||||||
|
filter: Object,
|
||||||
|
params: Object,
|
||||||
|
data: Object,
|
||||||
|
modal: Object,
|
||||||
|
timeopt: Object,
|
||||||
|
realtime: Object,
|
||||||
|
importdata: Object
|
||||||
|
})
|
||||||
|
|
||||||
|
const { $copy, $find, $findapi, $getapi, $setpage, $clone, $stripHtml, $snackbar, $dayjs } = useNuxtApp()
|
||||||
|
|
||||||
|
const showmodal = ref()
|
||||||
|
const pagedata = ref()
|
||||||
|
const newDataAvailable = ref(false)
|
||||||
|
const pendingNewData = ref(null)
|
||||||
|
const lastDataHash = ref(null)
|
||||||
|
const pollingInterval = ref(null)
|
||||||
|
|
||||||
|
let vpagename = props.pagename
|
||||||
|
let vfilter = props.filter ? $copy(props.filter) : undefined
|
||||||
|
let vparams = props.params ? $copy(props.params) : undefined
|
||||||
|
let connection = undefined
|
||||||
|
let optfilter = props.filter || (props.params ? props.params.filter : undefined)
|
||||||
|
|
||||||
|
const realtimeConfig = ref({ time: 0, update: "true" })
|
||||||
|
|
||||||
|
if (props.realtime) {
|
||||||
|
realtimeConfig.value = { time: props.realtime.time || 0, update: props.realtime.update }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vparams?.filter) {
|
||||||
|
for (const [key, value] of Object.entries(vparams.filter)) {
|
||||||
|
if (value.toString().indexOf('$') >= 0) {
|
||||||
|
vparams.filter[key] = store[value.replace('$', '')].id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateDataHash = (data) => {
|
||||||
|
if (!data) return null
|
||||||
|
try {
|
||||||
|
// Use a replacer with JSON.stringify to create a stable string representation
|
||||||
|
// by sorting the keys of any object. This ensures the hash is consistent
|
||||||
|
// even if the API returns objects with keys in a different order.
|
||||||
|
const replacer = (key, value) =>
|
||||||
|
value && typeof value === 'object' && !Array.isArray(value)
|
||||||
|
? Object.keys(value)
|
||||||
|
.sort()
|
||||||
|
.reduce((sorted, key) => {
|
||||||
|
sorted[key] = value[key];
|
||||||
|
return sorted;
|
||||||
|
}, {})
|
||||||
|
: value;
|
||||||
|
|
||||||
|
const stringToHash = JSON.stringify(data, replacer);
|
||||||
|
|
||||||
|
return stringToHash.split('').reduce((a, b) => {
|
||||||
|
a = ((a << 5) - a) + b.charCodeAt(0)
|
||||||
|
return a & a
|
||||||
|
}, 0)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error generating data hash:', e);
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startAutoCheck = () => {
|
||||||
|
if (pollingInterval.value) clearInterval(pollingInterval.value)
|
||||||
|
if (realtimeConfig.value.time && realtimeConfig.value.time > 0) {
|
||||||
|
pollingInterval.value = setInterval(() => checkDataChanges(), realtimeConfig.value.time * 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkDataChanges = async () => {
|
||||||
|
try {
|
||||||
|
const connlist = []
|
||||||
|
const conn1 = $findapi(props.api)
|
||||||
|
|
||||||
|
if (vfilter) {
|
||||||
|
const filter = $copy(conn1.params.filter) || {}
|
||||||
|
for (const [key, value] of Object.entries(vfilter)) {
|
||||||
|
filter[key] = value
|
||||||
|
}
|
||||||
|
for (const [key, value] of Object.entries(filter)) {
|
||||||
|
if (value.toString().indexOf('$') >= 0) {
|
||||||
|
filter[key] = store[value.replace('$', '')].id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn1.params.filter = filter
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vparams) conn1.params = $copy(vparams)
|
||||||
|
|
||||||
|
delete conn1.params.sort
|
||||||
|
delete conn1.params.values
|
||||||
|
|
||||||
|
conn1.params.summary = 'aggregate'
|
||||||
|
conn1.params.distinct_values = JSON.stringify({
|
||||||
|
total_count: { type: 'Count', field: 'id' },
|
||||||
|
last_updated: { type: 'Max', field: 'update_time' },
|
||||||
|
last_created: { type: 'Max', field: 'create_time' }
|
||||||
|
})
|
||||||
|
|
||||||
|
connlist.push(conn1)
|
||||||
|
|
||||||
|
const rs = await $getapi(connlist)
|
||||||
|
const obj = $find(rs, { name: props.api })
|
||||||
|
const newMetadata = obj ? obj.data.rows : {}
|
||||||
|
const newHash = generateDataHash(newMetadata)
|
||||||
|
|
||||||
|
if (lastDataHash.value === null) {
|
||||||
|
lastDataHash.value = newHash
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newHash !== lastDataHash.value) {
|
||||||
|
lastDataHash.value = newHash
|
||||||
|
|
||||||
|
if (realtimeConfig.value.update === "true") {
|
||||||
|
await loadFullDataAsync()
|
||||||
|
emit('dataUpdated', { newData: store[vpagename].data, autoUpdate: true, hasChanges: true })
|
||||||
|
} else {
|
||||||
|
newDataAvailable.value = true
|
||||||
|
emit('dataUpdated', { newData: null, autoUpdate: false, hasChanges: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking data:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadFullDataAsync = async () => {
|
||||||
|
try {
|
||||||
|
const connlist = []
|
||||||
|
const conn1 = $findapi(props.api)
|
||||||
|
|
||||||
|
if (vfilter) {
|
||||||
|
const filter = $copy(conn1.params.filter) || {}
|
||||||
|
for (const [key, value] of Object.entries(vfilter)) {
|
||||||
|
filter[key] = value
|
||||||
|
}
|
||||||
|
for (const [key, value] of Object.entries(filter)) {
|
||||||
|
if (value.toString().indexOf('$') >= 0) {
|
||||||
|
filter[key] = store[value.replace('$', '')].id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn1.params.filter = filter
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vparams) conn1.params = $copy(vparams)
|
||||||
|
|
||||||
|
delete conn1.params.summary
|
||||||
|
delete conn1.params.distinct_values
|
||||||
|
|
||||||
|
connlist.push(conn1)
|
||||||
|
|
||||||
|
const rs = await $getapi(connlist)
|
||||||
|
const obj = $find(rs, { name: props.api })
|
||||||
|
const newData = obj ? $copy(obj.data.rows) : []
|
||||||
|
|
||||||
|
updateDataDisplay(newData)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading full data:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openImportModal = () => {
|
||||||
|
const copy = $copy(props.importdata)
|
||||||
|
showmodal.value = copy
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateDataDisplay = (newData) => {
|
||||||
|
const copy = $clone(store[vpagename])
|
||||||
|
copy.data = newData
|
||||||
|
copy.update = { data: newData }
|
||||||
|
store.commit(vpagename, copy)
|
||||||
|
newDataAvailable.value = false
|
||||||
|
pendingNewData.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const manualRefresh = () => {
|
||||||
|
if (pendingNewData.value) {
|
||||||
|
updateDataDisplay(pendingNewData.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshData = async () => {
|
||||||
|
if (pollingInterval.value) clearInterval(pollingInterval.value);
|
||||||
|
newDataAvailable.value = false;
|
||||||
|
pendingNewData.value = null;
|
||||||
|
|
||||||
|
await getApi();
|
||||||
|
|
||||||
|
// After a manual refresh, force a metadata check to get the correct new hash.
|
||||||
|
lastDataHash.value = null;
|
||||||
|
await checkDataChanges();
|
||||||
|
|
||||||
|
newDataAvailable.value = false;
|
||||||
|
startAutoCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.realtime, (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
realtimeConfig.value.time = newVal.time || 0
|
||||||
|
realtimeConfig.value.update = newVal.update === true
|
||||||
|
startAutoCheck()
|
||||||
|
}
|
||||||
|
}, { deep: true })
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (pollingInterval.value) clearInterval(pollingInterval.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const timeOption = (v) => {
|
||||||
|
if (!v) return getApi()
|
||||||
|
|
||||||
|
if (v.filter_or) {
|
||||||
|
if (vfilter) vfilter = undefined
|
||||||
|
if (vparams) {
|
||||||
|
vparams.filter_or = v.filter_or
|
||||||
|
} else {
|
||||||
|
const found = $copy($findapi(props.api))
|
||||||
|
found.params.filter_or = v.filter_or
|
||||||
|
if (props.filter) {
|
||||||
|
const filter = $copy(props.filter)
|
||||||
|
for (const [key, value] of Object.entries(filter)) {
|
||||||
|
if (value.toString().indexOf('$') >= 0) {
|
||||||
|
filter[key] = store[value.replace('$', '')].id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found.params.filter = filter
|
||||||
|
}
|
||||||
|
vparams = found.params
|
||||||
|
}
|
||||||
|
return getApi()
|
||||||
|
}
|
||||||
|
|
||||||
|
let filter = vfilter ? vfilter : (props.params ? props.params.filter || {} : {})
|
||||||
|
for (const [key, value] of Object.entries(v.filter)) {
|
||||||
|
filter[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(filter)) {
|
||||||
|
if (value.toString().indexOf('$') >= 0) {
|
||||||
|
filter[key] = store[value.replace('$', '')].id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfilter) {
|
||||||
|
vfilter = filter
|
||||||
|
vparams = undefined
|
||||||
|
} else if (vparams) {
|
||||||
|
vparams.filter = filter
|
||||||
|
vparams.filter_or = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vfilter && !vparams) vfilter = filter
|
||||||
|
getApi()
|
||||||
|
}
|
||||||
|
|
||||||
|
const edit = (v) => {
|
||||||
|
const copy = props.modal ? $copy(props.modal) : {}
|
||||||
|
const f = copy.vbind ? copy.vbind : { pagename: vpagename, api: props.api, row: v }
|
||||||
|
f.pagename = vpagename
|
||||||
|
f.row = v
|
||||||
|
copy.vbind = f
|
||||||
|
showmodal.value = copy
|
||||||
|
}
|
||||||
|
|
||||||
|
const insert = () => {
|
||||||
|
const copy = props.modal ? $copy(props.modal) : {}
|
||||||
|
const f = copy.vbind ? copy.vbind : { pagename: vpagename, api: props.api }
|
||||||
|
f.pagename = vpagename
|
||||||
|
copy.vbind = f
|
||||||
|
showmodal.value = copy
|
||||||
|
}
|
||||||
|
|
||||||
|
const getApi = async () => {
|
||||||
|
const connlist = []
|
||||||
|
let row = props.setting?.id ? $copy(props.setting) : undefined
|
||||||
|
|
||||||
|
if (!row) {
|
||||||
|
const found = $find(store.settings.filter(v => v), props.setting > 0 ? { id: props.setting } : { name: props.setting })
|
||||||
|
if (found) row = $copy(found)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!row) {
|
||||||
|
const conn = $findapi('usersetting')
|
||||||
|
conn.params.filter = props.setting > 0 ? { id: props.setting } : { name: props.setting }
|
||||||
|
connlist.push(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = props.data ? $copy(props.data) : undefined
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
const conn1 = $findapi(props.api)
|
||||||
|
if (vfilter) {
|
||||||
|
const filter = conn1.params.filter || {}
|
||||||
|
for (const [key, value] of Object.entries(vfilter)) {
|
||||||
|
filter[key] = value
|
||||||
|
}
|
||||||
|
for (const [key, value] of Object.entries(filter)) {
|
||||||
|
if (value.toString().indexOf('$') >= 0) {
|
||||||
|
filter[key] = store[value.replace('$', '')].id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn1.params.filter = filter
|
||||||
|
}
|
||||||
|
if (vparams) conn1.params = vparams
|
||||||
|
connection = conn1
|
||||||
|
connlist.push(conn1)
|
||||||
|
}
|
||||||
|
|
||||||
|
let obj = undefined
|
||||||
|
if (connlist.length > 0) {
|
||||||
|
const rs = await $getapi(connlist)
|
||||||
|
const ele = $find(rs, { name: 'usersetting' })
|
||||||
|
if (ele) {
|
||||||
|
row = $find(ele.data.rows, { name: props.setting.name || props.setting })
|
||||||
|
const copy = $copy(store.settings)
|
||||||
|
copy.push(row)
|
||||||
|
store.commit('settings', copy)
|
||||||
|
}
|
||||||
|
obj = $find(rs, { name: props.api })
|
||||||
|
if (obj) data = $copy(obj.data.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
pagedata.value = $setpage(vpagename, row, obj)
|
||||||
|
const copy = $clone(pagedata.value)
|
||||||
|
copy.data = data
|
||||||
|
copy.update = { data: data }
|
||||||
|
store.commit(vpagename, copy)
|
||||||
|
|
||||||
|
// lastDataHash.value = generateDataHash(data); // This is now handled by checkDataChanges after a refresh.
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataEvent = (v, field, data) => {
|
||||||
|
if (data?.modal) {
|
||||||
|
const copy = $copy(data.modal)
|
||||||
|
const f = copy.vbind ? copy.vbind : {}
|
||||||
|
if (!f.api) f.api = props.api
|
||||||
|
f.pagename = vpagename
|
||||||
|
if (!f.row) f.row = v
|
||||||
|
copy.vbind = f
|
||||||
|
copy.field = field
|
||||||
|
showmodal.value = copy
|
||||||
|
}
|
||||||
|
emit('modalevent', { name: 'dataevent', data: { row: v, field: field } })
|
||||||
|
emit('dataevent', v, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportExcel = async () => {
|
||||||
|
if (!props.api) return
|
||||||
|
|
||||||
|
const found = $findapi('exportcsv')
|
||||||
|
found.params = connection.params
|
||||||
|
const fields = pagedata.value.fields
|
||||||
|
.filter(v => (v.show && v.export !== 'no') || v.export === 'yes')
|
||||||
|
.map(x => ({ name: x.name, label: $stripHtml(x.label) }))
|
||||||
|
found.params.fields = JSON.stringify(fields)
|
||||||
|
found.url = connection.url.replace('data/', 'exportcsv/')
|
||||||
|
const rs = await $getapi([found])
|
||||||
|
|
||||||
|
if (rs === 'error') {
|
||||||
|
$snackbar('Đã xảy ra lỗi. Vui lòng thử lại.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = window.URL.createObjectURL(new Blob([rs[0].data]))
|
||||||
|
const link = document.createElement('a')
|
||||||
|
const fileName = `${$dayjs(new Date()).format('YYYYMMDDhhmmss')}-data.csv`
|
||||||
|
link.href = url
|
||||||
|
link.setAttribute('download', fileName)
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
link.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!props.timeopt) await getApi()
|
||||||
|
startAutoCheck()
|
||||||
|
</script>
|
||||||
82
components/datatable/EditLabel.vue
Normal file
82
components/datatable/EditLabel.vue
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p class="fsb-20 mb-5">Điều chỉnh tiêu đề</p>
|
||||||
|
<div v-for="(v, i) in arr" :key="i" :class="(i>0? 'mt-4' : null)">
|
||||||
|
<p class="fsb-14">Dòng thứ {{(i+1)}}<span class="has-text-danger"> *</span></p>
|
||||||
|
<div class="field has-addons mt-1">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<input class="input" type="text" v-model="v.label">
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<a class="button px-2 is-primary" @click="add()">
|
||||||
|
<span>
|
||||||
|
<SvgIcon v-bind="{name: 'add1.png', type: 'white', size: 17}"></SvgIcon></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="control" @click="remove(i)" v-if="(i>0)">
|
||||||
|
<a class="button px-2 is-dark">
|
||||||
|
<span>
|
||||||
|
<SvgIcon v-bind="{name: 'bin.svg', type: 'white', size: 17}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-danger" v-if="v.error"> {{v.error}} </p>
|
||||||
|
</div>
|
||||||
|
<div class="buttons mt-5">
|
||||||
|
<button class="button is-primary has-text-white" @click="update()">Cập nhật</button>
|
||||||
|
<button class="button is-dark" @click="$emit('close')">Hủy bỏ</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['label'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
arr: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
let arr1 = this.label.replace('<div>', '').replace('</div>', '').split("</p>")
|
||||||
|
arr1.map(v=>{
|
||||||
|
if(!this.$empty(v)) {
|
||||||
|
let label = v + '</p>'
|
||||||
|
label = this.$stripHtml(label)
|
||||||
|
this.arr.push({label: label})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
add() {
|
||||||
|
this.arr.push({label: undefined})
|
||||||
|
},
|
||||||
|
remove(i) {
|
||||||
|
this.$remove(this.arr, i)
|
||||||
|
},
|
||||||
|
checkError() {
|
||||||
|
let error = false
|
||||||
|
this.arr.map(v=>{
|
||||||
|
if(this.$empty(v.label)) {
|
||||||
|
v.error = 'Nội dung không được bỏ trống'
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if(error) this.arr = this.$copy(this.arr)
|
||||||
|
return error
|
||||||
|
},
|
||||||
|
update() {
|
||||||
|
if(this.checkError()) return
|
||||||
|
let label = ''
|
||||||
|
if(this.arr.length>1) {
|
||||||
|
this.arr.map((v,i)=>{
|
||||||
|
label += `<p${i<this.arr.length-1? ' style="border-bottom: 1px solid white;"' : ''}>${v.label.trim()}</p>`
|
||||||
|
})
|
||||||
|
label = `<div>${label}</div>`
|
||||||
|
} else label = this.arr[0].label.trim()
|
||||||
|
this.$emit('modalevent', {name: 'label', data: label})
|
||||||
|
this.$emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
88
components/datatable/FieldAttribute.vue
Normal file
88
components/datatable/FieldAttribute.vue
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<template v-if="keys.length>0">
|
||||||
|
<div class="field is-horizontal" v-for="(v,i) in keys" :key="i">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field is-narrow">
|
||||||
|
<div class="control">
|
||||||
|
<input class="input fs-14" type="text" placeholder="" v-model="keys[i]">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<input class="input fs-14" type="text" placeholder="" v-model="values[i]">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-narrow">
|
||||||
|
<p class="control">
|
||||||
|
<a @click="addAttr()">
|
||||||
|
<SvgIcon v-bind="{name: 'add1.png', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a class="ml-2" @click="remove(i)">
|
||||||
|
<SvgIcon v-bind="{name: 'bin1.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a class="ml-2" @click="jsonData(v, i)">
|
||||||
|
<SvgIcon v-bind="{name: 'apps.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="mb-6" v-else>
|
||||||
|
<button class="button is-primary has-text-white" @click="addAttr()">Thêm thuộc tính</button>
|
||||||
|
</div>
|
||||||
|
<div class="buttons mt-5">
|
||||||
|
<a class="button is-primary has-text-white" @click="update()">Cập nhật</a>
|
||||||
|
</div>
|
||||||
|
<Modal @close="comp=undefined" @update="doUpdate"
|
||||||
|
v-bind="{component: comp, width: '40%', height: '300px', vbind: vbind}" v-if="comp"></Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['field', 'close'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
keys: [],
|
||||||
|
values: [],
|
||||||
|
comp: undefined,
|
||||||
|
vbind: undefined,
|
||||||
|
current: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
Object.keys(this.field).map(v=>{
|
||||||
|
this.keys.push(v)
|
||||||
|
this.values.push(this.field[v])
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
doUpdate(v) {
|
||||||
|
this.values[this.current.i] = v
|
||||||
|
},
|
||||||
|
jsonData(v, i) {
|
||||||
|
this.current = {v: v, i: i}
|
||||||
|
this.vbind = {field: this.$empty(this.values[i]) || typeof this.values[i] === 'string'? {} : this.values[i], close: true}
|
||||||
|
this.comp = 'datatable/FieldAttribute'
|
||||||
|
},
|
||||||
|
addAttr() {
|
||||||
|
this.keys.push(undefined)
|
||||||
|
this.values.push(undefined)
|
||||||
|
},
|
||||||
|
remove(i) {
|
||||||
|
this.$remove(this.keys, i)
|
||||||
|
this.$remove(this.values, i)
|
||||||
|
},
|
||||||
|
update() {
|
||||||
|
let obj = {}
|
||||||
|
this.keys.map((v,i)=>{
|
||||||
|
if(!this.$empty(v)) obj[v] = v.indexOf('__in')>0? this.values[i].split(',') : this.values[i]
|
||||||
|
})
|
||||||
|
this.$emit('update', obj)
|
||||||
|
this.$emit('modalevent', {name: 'update', data: obj})
|
||||||
|
if(this.close) this.$emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
170
components/datatable/FilterOption.vue
Normal file
170
components/datatable/FilterOption.vue
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="field mt-3 mb-1" v-if="field.format==='number'">
|
||||||
|
<label class="label fs-14">Chọn trường<span class="has-text-danger"> * </span> </label>
|
||||||
|
<div class="control">
|
||||||
|
<b-taginput
|
||||||
|
size="is-small"
|
||||||
|
v-model="tagsField"
|
||||||
|
:data="pageData? pageData.fields.filter(v=>v.format==='number') : []"
|
||||||
|
type="is-dark is-light"
|
||||||
|
autocomplete
|
||||||
|
:open-on-focus="true"
|
||||||
|
field="name"
|
||||||
|
icon="plus"
|
||||||
|
placeholder="Chọn trường"
|
||||||
|
>
|
||||||
|
<template slot-scope="props">
|
||||||
|
<span class="mr-3 has-text-danger has-text-weight-bold"> {{props.option.name}}</span>
|
||||||
|
<span :class="tagsField.find(v=>v.id===props.option.id)? 'has-text-dark' : ''"> {{$stripHtml(props.option.label, 60)}} </span>
|
||||||
|
</template>
|
||||||
|
<template slot="empty">
|
||||||
|
Không có trường thỏa mãn
|
||||||
|
</template>
|
||||||
|
</b-taginput>
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='tagsField')"> {{errors.find(v=>v.name==='tagsField').message}} </p>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2" v-if="tagsField.length>0">
|
||||||
|
<a @dblclick="expression = expression? (expression + ' ' + v.name) : v.name"
|
||||||
|
class="tag is-rounded" v-for="(v,i) in tagsField" :key="i">
|
||||||
|
<span class="tooltip">
|
||||||
|
{{ v.name }}
|
||||||
|
<span class="tooltiptext" style="top: 60%; bottom: unset; min-width: max-content; left: 25px;">{{ $stripHtml(v.label) }}</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal mt-3">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field" v-if="field.format==='number'">
|
||||||
|
<label class="label fs-14">Biểu thức có dạng Đúng / Sai <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control is-expanded">
|
||||||
|
<input class="input is-small" type="text" v-model="expression">
|
||||||
|
</p>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='expression')"> {{errors.find(v=>v.name==='expression').message}} </p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field" v-else>
|
||||||
|
<label class="label"> Chuỗi kí tự <span class="has-text-danger"> * </span>
|
||||||
|
</label>
|
||||||
|
<p class="control">
|
||||||
|
<input
|
||||||
|
class="input is-small"
|
||||||
|
type="text"
|
||||||
|
placeholder=""
|
||||||
|
v-model="searchText"
|
||||||
|
@change="changeStyle()"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="help has-text-danger"
|
||||||
|
v-if="errors.find((v) => v.name === 'searchText')"
|
||||||
|
>
|
||||||
|
{{ errors.find((v) => v.name === "searchText").msg }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field is-narrow" v-if="filterType==='color'">
|
||||||
|
<label class="label fs-14"> Mã màu <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input type="color" v-model="color" @change="changeStyle()">
|
||||||
|
</p>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='color')"> {{errors.find(v=>v.name==='color').message}} </p>
|
||||||
|
</div>
|
||||||
|
<div class="field is-narrow" v-else-if="filterType==='size'">
|
||||||
|
<label class="label fs-14"> Cỡ chữ <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input class="input is-small" type="text" placeholder="Nhập số" v-model="size" @change="changeStyle()">
|
||||||
|
</p>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='size')"> {{errors.find(v=>v.name==='size').message}} </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['filterObj', 'filterType', 'pagename', 'field'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tagsField: [],
|
||||||
|
expression: undefined,
|
||||||
|
form: undefined,
|
||||||
|
color: undefined,
|
||||||
|
size: undefined,
|
||||||
|
errors: [],
|
||||||
|
searchText: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.color = this.filterObj.color
|
||||||
|
this.size = this.filterObj.size
|
||||||
|
this.expression = this.filterObj.expression? this.filterObj.expression : this.field.name
|
||||||
|
if(this.filterObj.tags) {
|
||||||
|
this.filterObj.tags.map(v=>{
|
||||||
|
this.tagsField.push(this.pageData.fields.find(x=>x.name===v))
|
||||||
|
})
|
||||||
|
} else if(this.field.format==='number') this.tagsField.push(this.pageData.fields.find(v=>v.name===this.field.name))
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
expression: function(newVal) {
|
||||||
|
if(this.$empty(newVal)) return
|
||||||
|
else this.changeStyle()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
colorscheme: {
|
||||||
|
get: function() {return this.$store.state.colorscheme},
|
||||||
|
set: function(val) {this.$store.commit("updateColorScheme", {colorscheme: val})}
|
||||||
|
},
|
||||||
|
pageData: {
|
||||||
|
get: function() {return this.$store.state[this.pagename]},
|
||||||
|
set: function(val) {this.$store.commit('updateStore', {name: this.pagename, data: val})}
|
||||||
|
},
|
||||||
|
colorchoice: {
|
||||||
|
get: function() {return this.$store.state.colorchoice},
|
||||||
|
set: function(val) {this.$store.commit("updateColorChoice", {colorchoice: val})}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeStyle() {
|
||||||
|
let check = this.field.format==='number'? this.checkExpression() : this.checkCondition()
|
||||||
|
if(!check) return
|
||||||
|
var row = this.field.format==='number'? {expression: this.expression, tags: this.tagsField.map(v=>v.name)}
|
||||||
|
: {keyword: this.searchText, type: 'search'}
|
||||||
|
this.filterType==='color'? row.color = this.color : row.size = this.size
|
||||||
|
this.$emit('databack', row)
|
||||||
|
},
|
||||||
|
checkCondition() {
|
||||||
|
this.errors = []
|
||||||
|
if(this.filterType==='color' && this.$empty(this.color)) this.errors.push({name: 'color', message: 'Chọn màu'})
|
||||||
|
if(this.filterType==='size' && this.$empty(this.size)) this.errors.push({name: 'size', message: 'Nhập cỡ chữ'})
|
||||||
|
if(this.$empty(this.searchText)) this.errors.push({name: 'searchText', message: 'Chưa nhập chuỗi kí tự'})
|
||||||
|
return this.errors.length>0? false : true
|
||||||
|
},
|
||||||
|
checkExpression() {
|
||||||
|
this.errors = []
|
||||||
|
if(this.filterType==='color' && this.$empty(this.color)) this.errors.push({name: 'color', message: 'Chọn màu'})
|
||||||
|
if(this.filterType==='size' && this.$empty(this.size)) this.errors.push({name: 'size', message: 'Nhập cỡ chữ'})
|
||||||
|
let val = this.$copy(this.expression)
|
||||||
|
let exp = this.$copy(this.expression)
|
||||||
|
this.tagsField.forEach(v => {
|
||||||
|
let myRegExp = new RegExp(v.name, 'g')
|
||||||
|
val = val.replace(myRegExp, Math.random())
|
||||||
|
exp = exp.replace(myRegExp, "field.formatNumber(row['" + v.name + "'])")
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
let value = this.$calc(val)
|
||||||
|
if(isNaN(value) || value===Number.POSITIVE_INFINITY || value===Number.NEGATIVE_INFINITY) {
|
||||||
|
this.errors.push({name: 'expression', message: 'Biểu thức không hợp lệ'})
|
||||||
|
} else if(!(eval(value)===true || eval(value)===false)) {
|
||||||
|
this.errors.push({name: 'expression', message: 'Biểu thức không hợp lệ'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
this.errors.push({name: 'expression', message: 'Biểu thức không hợp lệ'})
|
||||||
|
}
|
||||||
|
return this.errors.length>0? false : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
207
components/datatable/FormatOption.vue
Normal file
207
components/datatable/FormatOption.vue
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="['bgcolor', 'color', 'textsize'].find((x) => x === sideBar)">
|
||||||
|
<p class="has-text-right has-text-grey is-italic fs-13">
|
||||||
|
Màu sắc sẽ hiển thị theo điều kiện Đúng / Sai, mã lệnh do hệ thống tự sinh
|
||||||
|
</p>
|
||||||
|
<div class="tabs is-boxed">
|
||||||
|
<ul>
|
||||||
|
<li v-for="(v, i) in tabs" :key="i" :class="tab.code === v.code ? 'is-active' : ''" @click="tab = v">
|
||||||
|
<a>{{ v.name }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="tab.code === 'expression' && ['bgcolor', 'color', 'textsize'].find((x) => x === sideBar)">
|
||||||
|
<template v-if="radio ? radio.code === 'condition' && sideBar === 'bgcolor' : false">
|
||||||
|
<div v-for="(v, i) in bgcolorFilter" :key="v.id" class="px-4">
|
||||||
|
<FilterOption
|
||||||
|
v-bind="{ filterObj: v, filterType: 'color', pagename: pagename, field: openField }"
|
||||||
|
:ref="v.id"
|
||||||
|
@databack="doConditionFilter($event, 'bgcolor', v.id)"
|
||||||
|
/>
|
||||||
|
<p class="fs-14 mt-1" :class="currentField.format === 'string' ? 'mb-1' : 'mb-2'">
|
||||||
|
<a class="has-text-primary mr-5" @click="addCondition(bgcolorFilter)" v-if="bgcolorFilter.length <= 30">
|
||||||
|
Thêm
|
||||||
|
</a>
|
||||||
|
<a class="has-text-danger" @click="removeCondition(bgcolorFilter, i)" v-if="bgcolorFilter.length > 1">
|
||||||
|
Bớt
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="radio ? radio.code === 'condition' && sideBar === 'color' : false">
|
||||||
|
<div v-for="(v, i) in colorFilter" :key="v.id" class="px-4">
|
||||||
|
<FilterOption
|
||||||
|
v-bind="{ filterObj: v, filterType: 'color', pagename: pagename, field: openField }"
|
||||||
|
:ref="v.id"
|
||||||
|
@databack="doConditionFilter($event, 'color', v.id)"
|
||||||
|
/>
|
||||||
|
<p class="fs-14 mt-1" :class="currentField.format === 'string' ? 'mb-1' : 'mb-2'">
|
||||||
|
<a class="has-text-primary mr-5" @click="addCondition(colorFilter)" v-if="colorFilter.length <= 30"> Thêm </a>
|
||||||
|
<a class="has-text-danger" @click="removeCondition(colorFilter, i)" v-if="colorFilter.length > 1"> Bớt </a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="radio ? radio.code === 'condition' && sideBar === 'textsize' : false">
|
||||||
|
<div v-for="(v, i) in sizeFilter" :key="v.id" class="px-4">
|
||||||
|
<FilterOption
|
||||||
|
v-bind="{ filterObj: v, filterType: 'size', pagename: pagename, field: openField }"
|
||||||
|
:ref="v.id"
|
||||||
|
@databack="doConditionFilter($event, 'textsize', v.id)"
|
||||||
|
/>
|
||||||
|
<p class="fs-14 mt-1" :class="currentField.format === 'string' ? 'mb-1' : 'mb-2'">
|
||||||
|
<a class="has-text-primary mr-5" @click="addCondition(sizeFilter)" v-if="sizeFilter.length <= 30"> Thêm </a>
|
||||||
|
<a class="has-text-danger" @click="removeCondition(sizeFilter, i)" v-if="sizeFilter.length > 1"> Bớt </a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="tab.code === 'script' && ['bgcolor', 'color', 'textsize'].find((x) => x === sideBar)">
|
||||||
|
<p class="my-4 mx-4">
|
||||||
|
<a @click="copyContent(script ? script : '')" class="mr-6">
|
||||||
|
<span class="icon-text">
|
||||||
|
<SvgIcon class="mr-2" v-bind="{ name: 'copy.svg', type: 'primary', siz: 18 }"></SvgIcon>
|
||||||
|
<span class="fs-16">Copy</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a @click="paste()" class="mr-6">
|
||||||
|
<span class="icon-text">
|
||||||
|
<SvgIcon class="mr-2" v-bind="{ name: 'pen1.svg', type: 'primary', siz: 18 }"></SvgIcon>
|
||||||
|
<span class="fs-16">Paste</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<div class="mx-4">
|
||||||
|
<textarea class="textarea fs-14" rows="8" v-model="script" @change="checkScript()" @dblclick="doCheck"></textarea>
|
||||||
|
</div>
|
||||||
|
<p class="mt-5 mx-4">
|
||||||
|
<span class="icon-text fsb-18">
|
||||||
|
Replace
|
||||||
|
<SvgIcon v-bind="{ name: 'right.svg', type: 'dark', size: 22 }"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<div class="field is-grouped mx-4 mt-4">
|
||||||
|
<div class="control">
|
||||||
|
<p class="fsb-14 mb-1">Đoạn text</p>
|
||||||
|
<input class="input" type="text" placeholder="" v-model="source" />
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<p class="fsb-14 mb-1">Thay bằng</p>
|
||||||
|
<input class="input" type="text" placeholder="" v-model="target" />
|
||||||
|
</div>
|
||||||
|
<div class="control pl-5">
|
||||||
|
<button class="button is-primary is-rounded is-outlined mt-5" @click="replace()">Replace</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-5 pt-2 mx-4">
|
||||||
|
<span class="icon-text fsb-18">
|
||||||
|
Thay đổi màu
|
||||||
|
<SvgIcon v-bind="{ name: 'right.svg', type: 'dark', size: 22 }"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="mx-4 mt-4"><button class="button is-primary is-rounded" @click="changeScript()">Cập nhật</button></p>
|
||||||
|
</template>
|
||||||
|
<TableOption v-bind="{ pagename: pagename }" v-else-if="sideBar === 'option'"> </TableOption>
|
||||||
|
<CreateTemplate v-else-if="sideBar === 'template'" v-bind="{ pagename: pagename, field: openField }">
|
||||||
|
</CreateTemplate>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
// FilterOption: () => import("@/components/datatable/FilterOption"),
|
||||||
|
// TableOption: () => import("@/components/datatable/TableOption"),
|
||||||
|
//CreateTemplate: () => import("@/components/datatable/CreateTemplate")
|
||||||
|
import CreateTemplate from "~/components/datatable/CreateTemplate";
|
||||||
|
const { $id, $copy, $empty, $stripHtml } = useNuxtApp();
|
||||||
|
var props = defineProps({
|
||||||
|
event: Object,
|
||||||
|
currentField: Object,
|
||||||
|
pagename: String,
|
||||||
|
});
|
||||||
|
var currentField = props.currentField;
|
||||||
|
var event = props.event;
|
||||||
|
var openField = {};
|
||||||
|
var bgcolorFilter = [];
|
||||||
|
var colorFilter = [];
|
||||||
|
var sizeFilter = [];
|
||||||
|
var sideBar = undefined;
|
||||||
|
var script = undefined;
|
||||||
|
var radio = undefined;
|
||||||
|
var tabs = [
|
||||||
|
{ code: "expression", name: "Biểu thức" },
|
||||||
|
{ code: "script", name: "Mã lệnh" },
|
||||||
|
];
|
||||||
|
var tab = { code: "expression", name: "Biểu thức" };
|
||||||
|
var source = undefined;
|
||||||
|
var target = $copy(currentField.name);
|
||||||
|
|
||||||
|
const initData = function () {
|
||||||
|
openField = event.field;
|
||||||
|
sideBar = event.name;
|
||||||
|
script = event.script;
|
||||||
|
radio = event.radio;
|
||||||
|
let field = event.field;
|
||||||
|
bgcolorFilter = [{ id: $id() }];
|
||||||
|
if (field.bgcolor) {
|
||||||
|
if (Array.isArray(field.bgcolor)) bgcolorFilter = $copy(field.bgcolor);
|
||||||
|
}
|
||||||
|
colorFilter = [{ id: $id() }];
|
||||||
|
if (field.color) {
|
||||||
|
if (Array.isArray(field.color)) colorFilter = $copy(field.color);
|
||||||
|
}
|
||||||
|
sizeFilter = [{ id: $id() }];
|
||||||
|
if (field.textsize) {
|
||||||
|
if (Array.isArray(field.textsize)) sizeFilter = field.textsize;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const doCheck = function () {
|
||||||
|
let text = window.getSelection().toString();
|
||||||
|
if ($empty(text)) return;
|
||||||
|
source = text;
|
||||||
|
};
|
||||||
|
const replace = function () {
|
||||||
|
if ($empty(script)) return;
|
||||||
|
script = script.replaceAll(source, target);
|
||||||
|
};
|
||||||
|
const paste = async function () {
|
||||||
|
script = await navigator.clipboard.readText();
|
||||||
|
};
|
||||||
|
const addCondition = function (arr) {
|
||||||
|
arr.push({ id: $id() });
|
||||||
|
};
|
||||||
|
const removeCondition = function (arr, i) {
|
||||||
|
$delete(arr, i);
|
||||||
|
};
|
||||||
|
const copyContent = function (value) {
|
||||||
|
$copyToClipboard(value);
|
||||||
|
};
|
||||||
|
const checkScript = function () {
|
||||||
|
if ($empty(script)) return;
|
||||||
|
try {
|
||||||
|
JSON.parse(script);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
const changeScript = function () {
|
||||||
|
if (!checkScript()) return;
|
||||||
|
let copy = $copy(openField);
|
||||||
|
copy[sideBar] = JSON.parse(script);
|
||||||
|
$emit("modalevent", { name: "updatefields", data: copy });
|
||||||
|
};
|
||||||
|
const doConditionFilter = function (v, type, id) {
|
||||||
|
v.id = id;
|
||||||
|
let copy = $copy(currentField);
|
||||||
|
if (copy[type] ? Array.isArray(copy[type]) : false) {
|
||||||
|
let idx = copy[type].findIndex((x) => x.id === id);
|
||||||
|
idx >= 0 ? (copy[type][idx] = v) : copy[type].push(v);
|
||||||
|
} else copy[type] = [v];
|
||||||
|
$emit("modalevent", { name: "updatefields", data: copy });
|
||||||
|
};
|
||||||
|
initData();
|
||||||
|
console.log(sideBar);
|
||||||
|
</script>
|
||||||
161
components/datatable/MenuSave.vue
Normal file
161
components/datatable/MenuSave.vue
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-4" v-if="currentsetting ? currentsetting.user === login.id : false">
|
||||||
|
<p class="fs-16 has-text-findata">
|
||||||
|
Đang mở: <b>{{ $stripHtml(currentsetting.name, 40) }}</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14">Chọn chế độ lưu <span class="has-text-danger"> * </span></label>
|
||||||
|
<div class="control is-expanded fs-14">
|
||||||
|
<a class="mr-5" v-if="isOverwrite()" @click="changeType('overwrite')">
|
||||||
|
<span class="icon-text">
|
||||||
|
<SvgIcon
|
||||||
|
v-bind="{
|
||||||
|
name: radioSave === 'overwrite' ? 'radio-checked.svg' : 'radio-unchecked.svg',
|
||||||
|
type: 'gray',
|
||||||
|
size: 22,
|
||||||
|
}"
|
||||||
|
></SvgIcon>
|
||||||
|
Ghi đè
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a @click="changeType('new')">
|
||||||
|
<span class="icon-text">
|
||||||
|
<SvgIcon
|
||||||
|
v-bind="{ name: radioSave === 'new' ? 'radio-checked.svg' : 'radio-unchecked.svg', type: 'gray', size: 22 }"
|
||||||
|
></SvgIcon>
|
||||||
|
Tạo mới
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template v-if="radioSave === 'new'">
|
||||||
|
<div class="field mt-4 px-0 mx-0">
|
||||||
|
<label class="label fs-14">Tên thiết lập <span class="has-text-danger"> * </span></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="" v-model="name" ref="name" v-on:keyup.enter="saveSetting" />
|
||||||
|
</div>
|
||||||
|
<div class="help has-text-danger" v-if="errors.find((v) => v.name === 'name')">
|
||||||
|
{{ errors.find((v) => v.name === "name").msg }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-4 px-0 mx-0">
|
||||||
|
<label class="label fs-14"> Mô tả </label>
|
||||||
|
<p class="control is-expanded">
|
||||||
|
<textarea class="textarea" rows="4" v-model="note"></textarea>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
|
<div class="field mt-4 px-0 mx-0">
|
||||||
|
<label class="label fs-14">Loại thiết lập <span class="has-text-danger"> * </span>
|
||||||
|
</label>
|
||||||
|
<div class="control is-expanded fs-14">
|
||||||
|
<span class="mr-4" v-for="(v,i) in $filter(store.settingtype, {code: ['private', 'public']})">
|
||||||
|
<a class="icon-text" @click="changeOption(v)">
|
||||||
|
<SvgIcon v-bind="{name: `radio-${radioOption===v.code? '' : 'un'}checked.svg`, type: radioOption===v.code? 'primary' : 'gray', size: 22}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
{{v.name}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
|
</template>
|
||||||
|
<div class="field mt-5 px-0 mx-0">
|
||||||
|
<label class="label fs-14" v-if="status !== undefined" :class="status ? 'has-text-primary' : 'has-text-danger'">
|
||||||
|
{{ status ? "Lưu thiết lập thành công." : "Lỗi. Lưu thiết lập thất bại." }}
|
||||||
|
</label>
|
||||||
|
<p class="control is-expanded">
|
||||||
|
<a class="button is-primary has-text-white" @click="saveSetting()">Lưu lại</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useStore } from "@/stores/index";
|
||||||
|
const emit = defineEmits([]);
|
||||||
|
const store = useStore();
|
||||||
|
var props = defineProps({
|
||||||
|
pagename: String,
|
||||||
|
classify: String,
|
||||||
|
option: String,
|
||||||
|
data: Object,
|
||||||
|
focus: Boolean,
|
||||||
|
});
|
||||||
|
const { $empty, $copy, $filter, $stripHtml, $updateapi, $insertapi, $findIndex, $snackbar } = useNuxtApp();
|
||||||
|
var pagename = props.pagename;
|
||||||
|
var radioOption = ref();
|
||||||
|
var login = { id: 1 };
|
||||||
|
var errors = [];
|
||||||
|
var radioType = undefined;
|
||||||
|
var radioDefault = 0;
|
||||||
|
var radioSave = ref("new");
|
||||||
|
var note = undefined;
|
||||||
|
var status = undefined;
|
||||||
|
var name = undefined;
|
||||||
|
var currentsetting = undefined;
|
||||||
|
var pagedata = store[props.pagename];
|
||||||
|
async function saveSetting() {
|
||||||
|
errors = [];
|
||||||
|
let detail = pagename ? { fields: pagedata.fields } : {};
|
||||||
|
if (pagename) {
|
||||||
|
let element = pagedata.tablesetting || {};
|
||||||
|
if (element !== store.originsetting) detail.tablesetting = element;
|
||||||
|
if (pagedata.filters ? pagedata.filters.length > 0 : false) {
|
||||||
|
detail.filters = pagedata.filters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (props.option) detail.option = props.option;
|
||||||
|
if (props.data) detail.data = props.data;
|
||||||
|
let data = {
|
||||||
|
user: login.id,
|
||||||
|
name: name,
|
||||||
|
detail: detail,
|
||||||
|
note: note,
|
||||||
|
type: radioType.id,
|
||||||
|
classify: props.classify ? props.classify : store.settingclass.find((v) => v.code === "data-field").id,
|
||||||
|
default: radioDefault,
|
||||||
|
update_time: new Date(),
|
||||||
|
};
|
||||||
|
let result;
|
||||||
|
if (radioSave.value === "new") {
|
||||||
|
if ($empty(name)) {
|
||||||
|
return errors.push({ name: "name", msg: "Tên thiết lập không được bỏ trống" });
|
||||||
|
}
|
||||||
|
result = await $insertapi("usersetting", data);
|
||||||
|
} else {
|
||||||
|
let copy = $copy(currentsetting);
|
||||||
|
copy.detail = detail;
|
||||||
|
copy.update_time = new Date();
|
||||||
|
result = await $updateapi("usersetting", copy);
|
||||||
|
}
|
||||||
|
if (radioSave.value === "new") {
|
||||||
|
emit("modalevent", { name: "opensetting", data: result });
|
||||||
|
} else {
|
||||||
|
let idx = $findIndex(store.settings, { id: result.id });
|
||||||
|
if (idx >= 0) {
|
||||||
|
let copy = $copy(store.settings);
|
||||||
|
copy[idx] = result;
|
||||||
|
store.commit("settings", copy);
|
||||||
|
}
|
||||||
|
$snackbar("Lưu thiết lập thành công");
|
||||||
|
emit("modalevent", { name: "updatesetting", data: result });
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function isOverwrite() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function changeType(value) {
|
||||||
|
radioSave.value = value;
|
||||||
|
}
|
||||||
|
function changeOption(v) {
|
||||||
|
radioOption.value = v.code;
|
||||||
|
}
|
||||||
|
function initData() {
|
||||||
|
radioType = store.settingtype.find((v) => v.code === "private");
|
||||||
|
if (props.pagename) currentsetting = $copy(pagedata.setting ? pagedata.setting : undefined);
|
||||||
|
if (!currentsetting) radioSave.value = "new";
|
||||||
|
else if (currentsetting.user !== login.id) radioSave.value = "new";
|
||||||
|
else radioSave.value = "overwrite";
|
||||||
|
}
|
||||||
|
initData();
|
||||||
|
</script>
|
||||||
159
components/datatable/ModelInfo.vue
Normal file
159
components/datatable/ModelInfo.vue
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tabs">
|
||||||
|
<ul>
|
||||||
|
<li :class="`${v.code === tab ? 'is-active has-text-weight-bold fs-18' : 'fs-18'}`" v-for="v in tabs">
|
||||||
|
<a @click="changeTab(v)">{{ v.name }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<template v-if="tab === 'datatype'">
|
||||||
|
<Caption class="mb-3" v-bind="{ title: 'Kiểu dữ liệu (type)', type: 'has-text-warning' }"></Caption>
|
||||||
|
<div class="py-1 border-bottom is-clickable" v-for="x in current.fields">
|
||||||
|
{{ x.name }}
|
||||||
|
<span class="ml-6 has-text-grey">{{ x.type }}</span>
|
||||||
|
<a class="ml-6 has-text-primary" v-if="x.model" @click="openModel(x)">{{ x.model }}</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="columns mx-0 mb-0 pb-0">
|
||||||
|
<div class="column is-7">
|
||||||
|
<Caption class="mb-2" v-bind="{ title: 'Values', type: 'has-text-warning' }"></Caption>
|
||||||
|
<input class="input" rows="1" v-model="values" />
|
||||||
|
</div>
|
||||||
|
<div class="column is-4s">
|
||||||
|
<Caption class="mb-2" v-bind="{ title: 'Filter', type: 'has-text-warning' }"></Caption>
|
||||||
|
<input class="input" rows="1" v-model="filter" />
|
||||||
|
</div>
|
||||||
|
<div class="column is-1">
|
||||||
|
<Caption class="mb-2" v-bind="{ title: 'Load', type: 'has-text-warning' }"></Caption>
|
||||||
|
<div>
|
||||||
|
<button class="button is-primary has-text-white" @click="loadData()">Load</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Caption class="mb-2" v-bind="{ title: 'Query', type: 'has-text-warning' }"></Caption>
|
||||||
|
<div class="mb-4">
|
||||||
|
{{ query }}
|
||||||
|
<a class="has-text-primary ml-5" @click="copy()">copy</a>
|
||||||
|
<p>
|
||||||
|
{{ apiUrl }}
|
||||||
|
<a class="has-text-primary ml-5" @click="$copyToClipboard(apiUrl)">copy</a>
|
||||||
|
<a class="has-text-primary ml-5" target="_blank" :href="apiUrl">open</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Caption class="mb-2" v-bind="{ title: 'Data', type: 'has-text-warning' }"></Caption>
|
||||||
|
<DataTable v-bind="{ pagename: pagename }" v-if="pagedata" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Modal @close="showmodal = undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { useStore } from "@/stores/index";
|
||||||
|
var props = defineProps({
|
||||||
|
data: Array,
|
||||||
|
info: Object,
|
||||||
|
});
|
||||||
|
const { $getdata, $getapi, $createField, $clone, $getpage, $empty, $copyToClipboard, $find } = useNuxtApp();
|
||||||
|
const store = useStore();
|
||||||
|
var pagename = "pagedata99";
|
||||||
|
var pagedata = ref();
|
||||||
|
pagedata.value = $getpage();
|
||||||
|
store.commit(pagename, pagedata);
|
||||||
|
let list = ["LogEntry", "Permission", "ContentType", "Session", "Group"];
|
||||||
|
var current = ref({ fields: [] });
|
||||||
|
var tabs = [
|
||||||
|
{ code: "datatype", name: "Kiểu dữ liệu" },
|
||||||
|
{ code: "table", name: "Dữ liệu" },
|
||||||
|
];
|
||||||
|
var tab = ref("datatype");
|
||||||
|
var datatable = ref();
|
||||||
|
var query = ref();
|
||||||
|
var values, filter;
|
||||||
|
var apiUrl = ref();
|
||||||
|
var showmodal = ref();
|
||||||
|
var data = props.data;
|
||||||
|
current.value = props.info;
|
||||||
|
function changeMenu(v) {
|
||||||
|
values = undefined;
|
||||||
|
filter = undefined;
|
||||||
|
current.value = v;
|
||||||
|
if (tab.value === "table") loadData();
|
||||||
|
}
|
||||||
|
async function changeTab(v) {
|
||||||
|
tab.value = v.code;
|
||||||
|
if (v.code === "table") loadData();
|
||||||
|
}
|
||||||
|
async function loadData() {
|
||||||
|
let vfilter = filter ? filter.trim() : undefined;
|
||||||
|
if (vfilter) {
|
||||||
|
try {
|
||||||
|
vfilter = JSON.parse(vfilter);
|
||||||
|
} catch (error) {
|
||||||
|
alert("Cấu trúc filter có lỗi");
|
||||||
|
vfilter = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let params = { values: values ? values.trim() : undefined, filter: filter };
|
||||||
|
let modelName = current.value.model;
|
||||||
|
let found = {
|
||||||
|
name: modelName.toLowerCase().replace("_", ""),
|
||||||
|
url: `data/${modelName}/`,
|
||||||
|
url_detail: `data-detail/${modelName}/`,
|
||||||
|
params: params,
|
||||||
|
};
|
||||||
|
query.value = $clone(found);
|
||||||
|
let rs = await $getapi([found]);
|
||||||
|
if (rs === "error") return alert("Đã xảy ra lỗi, hãy xem lại câu lệnh.");
|
||||||
|
datatable.value = rs[0].data.rows;
|
||||||
|
showData();
|
||||||
|
|
||||||
|
// api query
|
||||||
|
const baseUrl = "https://api.y99.vn/" + `${query.value.url}`;
|
||||||
|
apiUrl.value = baseUrl;
|
||||||
|
let vparams = !$empty(values) ? { values: values } : null;
|
||||||
|
if (!$empty(filter)) {
|
||||||
|
vparams = vparams ? { values: values, filter: filter } : { filter: filter };
|
||||||
|
}
|
||||||
|
if (vparams) {
|
||||||
|
let url = new URL(baseUrl);
|
||||||
|
let searchParams = new URLSearchParams(vparams);
|
||||||
|
url.search = searchParams.toString();
|
||||||
|
apiUrl.value = baseUrl + url.search;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showData() {
|
||||||
|
let arr = [];
|
||||||
|
if (!$empty(values)) {
|
||||||
|
let arr1 = values.trim().split(",");
|
||||||
|
arr1.map((v) => {
|
||||||
|
let val = v.trim();
|
||||||
|
let field = $createField(val, val, "string", true);
|
||||||
|
arr.push(field);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
current.value.fields.map((v) => {
|
||||||
|
let field = $createField(v.name, v.name, "string", true);
|
||||||
|
arr.push(field);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let clone = $clone(pagedata.value);
|
||||||
|
clone.fields = arr;
|
||||||
|
clone.data = datatable.value;
|
||||||
|
pagedata.value = undefined;
|
||||||
|
setTimeout(() => (pagedata.value = clone));
|
||||||
|
}
|
||||||
|
function copy() {
|
||||||
|
$copyToClipboard(JSON.stringify(query.value));
|
||||||
|
}
|
||||||
|
function openModel(x) {
|
||||||
|
showmodal.value = {
|
||||||
|
component: "datatable/ModelInfo",
|
||||||
|
title: x.model,
|
||||||
|
width: "70%",
|
||||||
|
height: "600px",
|
||||||
|
vbind: { data: data, info: $find(data, { model: x.model }) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
s
|
||||||
334
components/datatable/NewField.vue
Normal file
334
components/datatable/NewField.vue
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tabs is-boxed">
|
||||||
|
<ul>
|
||||||
|
<li :class="selectType.code===v.code? 'is-active fs-16' : 'fs-16'" v-for="v in fieldType">
|
||||||
|
<a @click="selectType = v"><span>{{ v.name }}</span></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<template v-if="selectType.code==='formula'">
|
||||||
|
<b-radio :class="i===1? 'ml-5' : null" v-model="choice" v-for="(v,i) in choices" :key="i"
|
||||||
|
:native-value="v.code">
|
||||||
|
<span :class="v.code===choice? 'fsb-16' : 'fs-16'">{{v.name}}</span>
|
||||||
|
</b-radio>
|
||||||
|
<div class="has-background-light mt-3 px-3 py-3">
|
||||||
|
<div class="tags are-medium mb-0" v-if="choice==='function'">
|
||||||
|
<span :class="`tag ${func===v.code? 'is-primary' : 'is-dark'} is-rounded is-clickable`"
|
||||||
|
v-for="(v,i) in funcs" :key="i" @click="changeFunc(v)" @dblclick="addFunc(v)">{{v.name}}</span>
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<div class="field px-0 mx-0">
|
||||||
|
<label class="label fs-14">Chọn trường<span class="has-text-danger"> *</span> </label>
|
||||||
|
<div class="control">
|
||||||
|
<!--<b-taginput
|
||||||
|
size="is-small"
|
||||||
|
v-model="tags"
|
||||||
|
:data="fields.filter(v=>v.format==='number')"
|
||||||
|
type="is-dark is-light"
|
||||||
|
autocomplete
|
||||||
|
:open-on-focus="true"
|
||||||
|
field="caption"
|
||||||
|
icon="plus"
|
||||||
|
placeholder="Chọn trường"
|
||||||
|
>
|
||||||
|
<template slot-scope="props">
|
||||||
|
<span class="mr-3 has-text-danger">{{props.option.name}}</span>
|
||||||
|
<span :class="tags.find(v=>v.id===props.option.id)? 'has-text-dark' : ''">{{$stripHtml(props.option.label,50)}}</span>
|
||||||
|
</template>
|
||||||
|
<template slot="empty">
|
||||||
|
Không có trường thỏa mãn
|
||||||
|
</template>
|
||||||
|
</b-taginput>-->
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='tags')"> {{errors.find(v=>v.name==='tags').message}} </p>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-3" v-if="tags.length>0">
|
||||||
|
<p class="help is-primary mb-1">Click đúp vào để thêm vào công thức tính.</p>
|
||||||
|
<div class="tags mb-2">
|
||||||
|
<span @dblclick="formula = formula? (formula + ' ' + v.name) : v.name" class="tag is-dark is-rounded is-clickable"
|
||||||
|
v-for="v in tags">
|
||||||
|
{{v.name}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="tags">
|
||||||
|
<span v-for="(v,i) in operator" :key="i">
|
||||||
|
<span @dblclick="addOperator(v)" class="tag is-primary is-rounded is-clickable mr-4">
|
||||||
|
<span class="fs-16">{{v.code}}</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="field mt-3 px-0 mx-0">
|
||||||
|
<label class="label fs-14">Công thức tính <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control">
|
||||||
|
<textarea class="textarea" rows="3" type="text" :placeholder="placeholder" v-model="formula"> </textarea>
|
||||||
|
</p>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='formula')"> {{errors.find(v=>v.name==='formula').message}} </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal mt-3 px-0 mx-0">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14">Hiển thị theo <span class="has-text-danger"> * </span> </label>
|
||||||
|
<div class="control">
|
||||||
|
<b-autocomplete
|
||||||
|
size="is-small"
|
||||||
|
icon-right="magnify"
|
||||||
|
:value="selectUnit? selectUnit.name : ''"
|
||||||
|
placeholder=""
|
||||||
|
:keep-first=true
|
||||||
|
:open-on-focus=true
|
||||||
|
:data="moneyunit"
|
||||||
|
field="name"
|
||||||
|
@select="option => selectUnit = option">
|
||||||
|
</b-autocomplete>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14">Phần thập phân</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input is-small" type="text" placeholder="" v-model="decimal">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="field px-0 mx-0">
|
||||||
|
<label class="label">Tên trường <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control">
|
||||||
|
<input class="input" type="text" placeholder="Tên trường phải là duy nhất" v-model="name"
|
||||||
|
:readonly="selectType? selectType.code==='formula': false">
|
||||||
|
</p>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='name')"> {{errors.find(v=>v.name==='name').message}} </p>
|
||||||
|
<p class="help has-text-primary" v-else> Tên trường do hệ thống tự sinh.</p>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5">
|
||||||
|
<label class="label">Mô tả<span class="has-text-danger"> *</span></label>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded" >
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
v-model="label"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button" @click="editLabel()">
|
||||||
|
<span><SvgIcon v-bind="{name: 'pen.svg', type: 'dark', size: 17}"></SvgIcon></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-danger" v-if="errors.find(v=>v.name==='label')"> {{errors.find(v=>v.name==='label').message}} </p>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-5" v-if="selectType.code==='empty'">
|
||||||
|
<label class="label"
|
||||||
|
>Kiểu dữ liệu
|
||||||
|
<span class="has-text-danger"> * </span>
|
||||||
|
</label>
|
||||||
|
<div class="control fs-14">
|
||||||
|
<span class="mr-4" v-for="(v,i) in datatype">
|
||||||
|
<a class="icon-text" @click="changeType(v)">
|
||||||
|
<SvgIcon v-bind="{name: `radio-${radioType.code===v.code? '' : 'un'}checked.svg`, type: 'gray', size: 22}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
{{v.name}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-5">
|
||||||
|
<p class="control">
|
||||||
|
<a class="button is-primary has-text-white"
|
||||||
|
@click="selectType.code==='formula'? createField() : createEmptyField()">Tạo cột</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Modal v-bind="showmodal" v-if="showmodal" @label="changeLabel" @close="close"></Modal>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { useStore } from '@/stores/index'
|
||||||
|
import ScrollBox from '~/components/datatable/ScrollBox'
|
||||||
|
const emit = defineEmits(['modalevent'])
|
||||||
|
const store = useStore()
|
||||||
|
const { $id, $copy, $clone, $empty, $stripHtml, $createField, $calc, $isNumber } = useNuxtApp()
|
||||||
|
var props = defineProps({
|
||||||
|
pagename: String,
|
||||||
|
field: Object,
|
||||||
|
filters: Object,
|
||||||
|
filterData: Object,
|
||||||
|
width: String
|
||||||
|
})
|
||||||
|
const moneyunit = store.moneyunit
|
||||||
|
const datatype = store.datatype
|
||||||
|
var showmodal = ref()
|
||||||
|
var pagedata = store[props.pagename]
|
||||||
|
var selectUnit = moneyunit.find(v=>v.code==='one')
|
||||||
|
var data = []
|
||||||
|
var current = 1
|
||||||
|
var filterData = []
|
||||||
|
var loading = false
|
||||||
|
var fieldType = [{code: 'formula', name: 'Tạo công thức'}, {code: 'empty', name: 'Tạo cột rỗng'}]
|
||||||
|
var errors = []
|
||||||
|
var tags = []
|
||||||
|
var formula = undefined
|
||||||
|
var name = `f${$id().toLocaleLowerCase()}`
|
||||||
|
var label = undefined
|
||||||
|
var errors = []
|
||||||
|
var selectType = fieldType.find(v=>v.code==='empty')
|
||||||
|
var radioType = ref(datatype.find(v=>v.code==='string'))
|
||||||
|
var fields = []
|
||||||
|
var options = undefined
|
||||||
|
var columns = $copy(pagedata.fields.filter(v=>v.format==='number'))
|
||||||
|
var decimal = undefined
|
||||||
|
var choices = [{code: 'column', name: 'Dùng cột dữ liệu'}, {code: 'function', name: 'Dùng hàm số'}]
|
||||||
|
var choice = 'column'
|
||||||
|
var funcs = [{code: 'sum', name: 'Sum'}, {code: 'max', name: 'Max'}, {code: 'min', name: 'Min'}, {code: 'avg', name: 'Avg'}]
|
||||||
|
var func = 'sum'
|
||||||
|
var placeholder = 'Minh hoạ công thức: f10001 + f10002'
|
||||||
|
var args = undefined
|
||||||
|
var operator = [{code: '+', name: 'Cộng'}, {code: '-', name: 'Trừ'}, {code: '*', name: 'Nhân'}, {code: '/', name: 'Chia'}, {code: '>', name: 'Lớn hơn'},
|
||||||
|
{code: '>=', name: 'Lớn hơn hoặc bằng'}, {code: '<', name: 'Nhỏ hơn'}, {code: '<=', name: 'Nhỏ hơn hoặc bằng'}, {code: '==', name: 'Bằng'},
|
||||||
|
{code: '&&', name: 'Và'}, {code: '||', name: 'Hoặc'}, {code: 'iif', name: 'Điều kiện rẽ nhánh'}]
|
||||||
|
function editLabel() {
|
||||||
|
if($empty(label)) return
|
||||||
|
showmodal.value = {component: 'datatable/EditLabel', width: '500px', height: '300px', vbind: {label: label}}
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
showmodal.value = null
|
||||||
|
}
|
||||||
|
function changeLabel(evt) {
|
||||||
|
label = evt
|
||||||
|
showmodal.value = null
|
||||||
|
}
|
||||||
|
function changeType(v) {
|
||||||
|
radioType.value = v
|
||||||
|
}
|
||||||
|
function addFunc(v) {
|
||||||
|
formula = (formula? formula + ' ' : '') + v.name + '(C0: C2)'
|
||||||
|
}
|
||||||
|
function addOperator(v) {
|
||||||
|
let text = v.code==='iif'? 'a>b? c : d' : v.code
|
||||||
|
formula = `${formula || ''} ${text}`
|
||||||
|
}
|
||||||
|
function changeFunc(v) {
|
||||||
|
placeholder = `${v.name}(C0:C2) hoặc ${v.name}(C0,C1,C2). C là viết tắt của cột dữ liệu, số thứ tự của cột bắt đầu từ 0`
|
||||||
|
func = v.code
|
||||||
|
}
|
||||||
|
function getFields() {
|
||||||
|
fields = pagedata? $copy(pagedata.fields) : []
|
||||||
|
fields.map(v=>v.caption = (v.label? v.label.indexOf('<')>=0 : false)? v.name : v.label)
|
||||||
|
}
|
||||||
|
function checkFunc() {
|
||||||
|
let error = false
|
||||||
|
let val = formula.trim().replaceAll(' ', '')
|
||||||
|
if(val.toLowerCase().indexOf(func)<0) error = true
|
||||||
|
let start = val.toLowerCase().indexOf('(')
|
||||||
|
let end = val.toLowerCase().indexOf(')')
|
||||||
|
if( start<0 || end<0) error = true
|
||||||
|
let content = val.substring(start+1, end)
|
||||||
|
if($empty(content)) error = true
|
||||||
|
let content1 = content.replaceAll(':', ',')
|
||||||
|
let arr = content1.split(',')
|
||||||
|
arr.map(v=>{
|
||||||
|
let arr1 = v.toLowerCase().split('c')
|
||||||
|
if(arr1.length!==2) error = true
|
||||||
|
else if(!$isNumber(arr1[1])) error = true
|
||||||
|
})
|
||||||
|
return error? 'error' : content
|
||||||
|
}
|
||||||
|
function checkValid() {
|
||||||
|
errors = []
|
||||||
|
if(tags.length===0 && choice==='column') {
|
||||||
|
errors.push({name: 'tags', message: 'Chưa chọn trường xây dựng công thức.'})
|
||||||
|
}
|
||||||
|
if(!$empty(formula)? $empty(formula.trim()) : true) {
|
||||||
|
errors.push({name: 'formula', message: 'Công thức không được bỏ trống.'})
|
||||||
|
}
|
||||||
|
if(!$empty(label)? $empty(label.trim()) : true )
|
||||||
|
errors.push({name: 'label', message: 'Mô tả không được bỏ trống.'})
|
||||||
|
else if(pagedata.fields.find(v=>v.label.toLowerCase()===label.toLowerCase())) {
|
||||||
|
errors.push({name: 'label', message: 'Mô tả bị trùng. Hãy đặt mô tả khác.'})
|
||||||
|
}
|
||||||
|
if(errors.length>0) return false
|
||||||
|
//check formula in case use column
|
||||||
|
if(choice==='column') {
|
||||||
|
let val = $copy(formula)
|
||||||
|
tags.forEach(v => {
|
||||||
|
let myRegExp = new RegExp(v.name, 'g')
|
||||||
|
val = val.replace(myRegExp, Math.random())
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
let value = $calc(val)
|
||||||
|
if(isNaN(value) || value===Number.POSITIVE_INFINITY || value===Number.NEGATIVE_INFINITY) {
|
||||||
|
errors.push({name: 'formula', message: 'Công thức không hợp lệ'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
console.log(err)
|
||||||
|
errors.push({name: 'formula', message: 'Công thức không hợp lệ'})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(checkFunc()==='error') errors.push({name: 'formula', message: `Hàm ${func.toUpperCase()} không hợp lệ`})
|
||||||
|
}
|
||||||
|
return errors.length>0? false : true
|
||||||
|
}
|
||||||
|
function createField() {
|
||||||
|
if(!checkValid()) return
|
||||||
|
let field = $createField(name.trim(), label.trim(), 'number', true)
|
||||||
|
field.formula = formula.trim().replaceAll(' ', '')
|
||||||
|
if(choice==='function') {
|
||||||
|
field.func = func
|
||||||
|
field.vals = checkFunc()
|
||||||
|
} else field.tags = tags.map(v=>v.name)
|
||||||
|
field.level = Math.max(...pagedata.fields.map(v=>v.level? v.level : 0)) + 1
|
||||||
|
field.unit = selectUnit.detail
|
||||||
|
field.decimal = decimal
|
||||||
|
field.disable = 'search,value'
|
||||||
|
let copy = $copy(pagedata)
|
||||||
|
copy.fields.push(field)
|
||||||
|
store.commit(props.pagename, copy)
|
||||||
|
emit('newfield', field)
|
||||||
|
tags = []
|
||||||
|
formula = undefined
|
||||||
|
label = undefined
|
||||||
|
name = `f${$id()}`
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
function createEmptyField() {
|
||||||
|
errors = []
|
||||||
|
if(!$empty(name)? $empty(name.trim()) : true )
|
||||||
|
errors.push({name: 'name', message: 'Tên không được bỏ trống.'})
|
||||||
|
else if(pagedata.fields.find(v=>v.name.toLowerCase()===name.toLowerCase())) {
|
||||||
|
errors.push({name: 'name', message: 'Tên trường bị trùng. Hãy đặt tên khác.'})
|
||||||
|
}
|
||||||
|
if(!$empty(label)? $empty(label.trim()) : true )
|
||||||
|
errors.push({name: 'label', message: 'Mô tả không được bỏ trống.'})
|
||||||
|
else if(pagedata.fields.find(v=>v.label.toLowerCase()===label.toLowerCase())) {
|
||||||
|
errors.push({name: 'label', message: 'Mô tả bị trùng. Hãy đặt mô tả khác.'})
|
||||||
|
}
|
||||||
|
if(errors.length>0) return
|
||||||
|
let field = $createField(name.trim(), label.trim(), radioType.value.code, true)
|
||||||
|
if(selectType.code==='chart') field = createChartField()
|
||||||
|
let copy = $clone(pagedata)
|
||||||
|
copy.fields.push(field)
|
||||||
|
copy.update = {fields: copy.fields}
|
||||||
|
store.commit(props.pagename, copy)
|
||||||
|
//pagedata = copy
|
||||||
|
emit('newfield', field)
|
||||||
|
label = undefined
|
||||||
|
name = `f${$id()}`
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
function createChartField() {
|
||||||
|
let array = pagedata.fields.filter(v=>v.format==='number' && v.show)
|
||||||
|
if(args) array = $copy(args)
|
||||||
|
let text = ''
|
||||||
|
array.map((v,i)=>text += `'${v.name}${i<array.length-1? "'," : "'"}`)
|
||||||
|
let label = ''
|
||||||
|
array.map((v,i)=>label += `'${$stripHtml(v.label)}${i<array.length-1? "'," : "'"}`)
|
||||||
|
let field = $createField(name.trim(), label.trim(), radioType.value.code, true)
|
||||||
|
field.chart = 'yes'
|
||||||
|
field.template = `<TrendingChart class="is-clickable" v-bind="{row: row, fields: [${text}], labels: [${label}], width: '80', height: '26', 'header': ['stock_code', 'name']}"/>`
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
//============
|
||||||
|
getFields()
|
||||||
|
</script>
|
||||||
64
components/datatable/Pagination.vue
Normal file
64
components/datatable/Pagination.vue
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="pagination mx-0" role="navigation" aria-label="pagination">
|
||||||
|
<ul class="pagination-list" v-if="pageInfo">
|
||||||
|
<li v-for="v in pageInfo">
|
||||||
|
<a v-if="currentPage===v" class="pagination-link is-current has-background-primary has-text-white" :aria-label="`Page ${v}`" aria-current="page">{{ v }}</a>
|
||||||
|
<a v-else href="#" class="pagination-link" :aria-label="`Goto page ${v}`" @click="changePage(v)">{{ v }}</a>
|
||||||
|
</li>
|
||||||
|
<a @click="previous()" class="pagination-previous ml-5">
|
||||||
|
<SvgIcon v-bind="{name: 'left1.svg', type: 'dark', size: 20, alt: 'Tìm kiếm'}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a @click="next()" class="pagination-next">
|
||||||
|
<SvgIcon v-bind="{name: 'right.svg', type: 'dark', size: 20, alt: 'Tìm kiếm'}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const emit = defineEmits(['changepage'])
|
||||||
|
var props = defineProps({
|
||||||
|
data: Array,
|
||||||
|
perPage: Number
|
||||||
|
})
|
||||||
|
var currentPage = 1
|
||||||
|
var totalRows = props.data.length
|
||||||
|
var lastPage = parseInt(totalRows / props.perPage)
|
||||||
|
if(lastPage*props.perPage<totalRows) lastPage += 1
|
||||||
|
var pageInfo = ref()
|
||||||
|
function pages(current_page, last_page, onSides = 2) {
|
||||||
|
// pages
|
||||||
|
let pages = [];
|
||||||
|
// Loop through
|
||||||
|
for (let i = 1; i <= last_page; i++) {
|
||||||
|
// Define offset
|
||||||
|
let offset = (i == 1 || last_page) ? onSides + 1 : onSides;
|
||||||
|
// If added
|
||||||
|
if (i == 1 || (current_page - offset <= i && current_page + offset >= i) ||
|
||||||
|
i == current_page || i == last_page) {
|
||||||
|
pages.push(i);
|
||||||
|
} else if (i == current_page - (offset + 1) || i == current_page + (offset + 1)) {
|
||||||
|
pages.push('...');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
function changePage(page) {
|
||||||
|
if(page==='...') return
|
||||||
|
currentPage = page
|
||||||
|
pageInfo.value = pages(page, lastPage, 2)
|
||||||
|
emit('changepage', page)
|
||||||
|
}
|
||||||
|
pageInfo.value = pages(1, lastPage, 2)
|
||||||
|
watch(() => props.data, (newVal, oldVal) => {
|
||||||
|
totalRows = props.data.length
|
||||||
|
lastPage = parseInt(totalRows / props.perPage)
|
||||||
|
if(lastPage*props.perPage<totalRows) lastPage += 1
|
||||||
|
pageInfo.value = pages(1, lastPage, 2)
|
||||||
|
})
|
||||||
|
function previous() {
|
||||||
|
if(currentPage>1) changePage(currentPage-1)
|
||||||
|
}
|
||||||
|
function next() {
|
||||||
|
if(currentPage<lastPage) changePage(currentPage+1)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
111
components/datatable/ScrollBox.vue
Normal file
111
components/datatable/ScrollBox.vue
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<template>
|
||||||
|
<div class="px-2" :style="`max-height: ${maxheight}; overflow-y: auto;`">
|
||||||
|
<div class="field is-grouped py-1 border-bottom my-0" v-for="(v, i) in rows" :key="i">
|
||||||
|
<p class="control is-expanded py-0 fs-14 hyperlink" @click="doClick(v,i)">
|
||||||
|
{{ $stripHtml(v[name] || v.fullname || v.code || 'n/a', 75) }}
|
||||||
|
<span class="icon has-text-primary" v-if="checked[i] && notick!==true">
|
||||||
|
<SvgIcon v-bind="{name: 'tick.svg', type: 'primary', size: 15}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="control py-0" v-if="show">
|
||||||
|
<span class="icon-text has-text-grey mr-2 fs-13" v-if="show.author">
|
||||||
|
<SvgIcon v-bind="{name: 'user.svg', type: 'gray', size: 15}"></SvgIcon>
|
||||||
|
<span>{{ v[show.author] }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="icon-text has-text-grey mr-2 fs-13" v-if="show.view">
|
||||||
|
<SvgIcon v-bind="{name: 'view.svg', type: 'gray', size: 15}"></SvgIcon>
|
||||||
|
<span>{{ v[show.view] }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="fs-13 has-text-grey" v-if="show.time">{{$dayjs(v['create_time']).fromNow(true)}}</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="icon ml-1" v-if="show.link" @click="doClick(v,i, 'newtab')">
|
||||||
|
<SvgIcon v-bind="{name: 'opennew.svg', type: 'gray', size: 15}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span class="tooltiptext">Mở trong tab mớ</span>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip" v-if="show.rename">
|
||||||
|
<a class="icon ml-1" @click="$emit('rename', v, i)">
|
||||||
|
<SvgIcon v-bind="{name: 'pen1.svg', type: 'gray', size: 15}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span class="tooltiptext">Đổi tên</span>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip" v-if="show.rename">
|
||||||
|
<a class="icon has-text-danger ml-1" @click="$emit('remove', v, i)">
|
||||||
|
<SvgIcon v-bind="{name: 'bin1.svg', type: 'gray', size: 15}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span class="tooltiptext">Xóa</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['data', 'name', 'maxheight', 'perpage', 'sort', 'selects', 'keyval', 'show', 'notick'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 1,
|
||||||
|
total: this.data.length,
|
||||||
|
rows: this.data.slice(0, this.perpage),
|
||||||
|
selected: [],
|
||||||
|
checked: {},
|
||||||
|
time: undefined,
|
||||||
|
array: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getdata()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
data: function(newVal) {
|
||||||
|
this.getdata()
|
||||||
|
},
|
||||||
|
selects: function(newVal) {
|
||||||
|
this.getSelect()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getdata() {
|
||||||
|
this.currentPage = 1
|
||||||
|
this.array = this.$copy(this.data)
|
||||||
|
if(this.sort!==false) {
|
||||||
|
let f = {}
|
||||||
|
let showtime = this.show? this.show.time : false
|
||||||
|
showtime? f['create_time'] = 'desc' : f[this.name] = 'asc'
|
||||||
|
this.$multiSort(this.array, f)
|
||||||
|
}
|
||||||
|
this.rows = this.array.slice(0, this.perpage)
|
||||||
|
this.getSelect()
|
||||||
|
},
|
||||||
|
getSelect() {
|
||||||
|
if(!this.selects) return
|
||||||
|
this.selected = []
|
||||||
|
this.checked = {}
|
||||||
|
this.selects.map(v=>{
|
||||||
|
let idx = this.rows.findIndex(x=>x[this.keyval? this.keyval : this.name]===v)
|
||||||
|
if(idx>=0) {
|
||||||
|
this.selected.push(this.rows[idx])
|
||||||
|
this.checked[idx] = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
doClick(v, i, type) {
|
||||||
|
this.checked[i] = this.checked[i]? false : true
|
||||||
|
this.checked = this.$copy(this.checked)
|
||||||
|
let idx = this.selected.findIndex(x=>x.id===v.id)
|
||||||
|
idx>=0? this.$remove(this.selected) : this.selected.push(v)
|
||||||
|
this.$emit('selected', v, type)
|
||||||
|
},
|
||||||
|
handleScroll(e) {
|
||||||
|
const bottom = e.target.scrollHeight - e.target.scrollTop -5 < e.target.clientHeight
|
||||||
|
if (bottom) {
|
||||||
|
if(this.total? this.total>this.rows.length : true) {
|
||||||
|
this.currentPage +=1
|
||||||
|
let arr = this.array.filter((ele,index) => (index>=(this.currentPage-1)*this.perpage && index<this.currentPage*this.perpage))
|
||||||
|
this.rows = this.rows.concat(arr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
99
components/datatable/TableOption.vue
Normal file
99
components/datatable/TableOption.vue
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr class="fs-14">
|
||||||
|
<th>#</th>
|
||||||
|
<th>Tên trường</th>
|
||||||
|
<th>Tên cột</th>
|
||||||
|
<th>...</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="fs-14" v-for="(v, i) in fields">
|
||||||
|
<td>{{ i }}</td>
|
||||||
|
<td>
|
||||||
|
<a class="has-text-primary" @click="openField(v, i)">{{ v.name }}</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ $stripHtml(v.label, 50) }}</td>
|
||||||
|
<td>
|
||||||
|
<a class="mr-4" @click="moveDown(v, i)">
|
||||||
|
<SvgIcon v-bind="{ name: 'down1.png', type: 'dark', size: 18 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a class="mr-4" @click="moveUp(v, i)">
|
||||||
|
<SvgIcon v-bind="{ name: 'up.png', type: 'dark', size: 18 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a @click="askConfirm(v, i)">
|
||||||
|
<SvgIcon v-bind="{ name: 'bin1.svg', type: 'dark', size: 18 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<Modal @close="showmodal = undefined" @update="update" @confirm="remove" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { useStore } from "@/stores/index";
|
||||||
|
const emit = defineEmits(["close"]);
|
||||||
|
const { $stripHtml, $clone, $arrayMove, $remove } = useNuxtApp();
|
||||||
|
const store = useStore();
|
||||||
|
var props = defineProps({
|
||||||
|
pagename: String,
|
||||||
|
});
|
||||||
|
var showmodal = ref();
|
||||||
|
var current;
|
||||||
|
var pagedata = store[props.pagename];
|
||||||
|
var fields = ref(pagedata.fields);
|
||||||
|
function openField(v, i) {
|
||||||
|
current = { v: v, i: i };
|
||||||
|
showmodal.value = {
|
||||||
|
component: "datatable/FieldAttribute",
|
||||||
|
title: `${v.name} / ${$stripHtml(v.label)}`,
|
||||||
|
width: "50%",
|
||||||
|
height: "400px",
|
||||||
|
vbind: { field: v },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function update(data) {
|
||||||
|
fields.value[current.i] = data;
|
||||||
|
let copy = $clone(pagedata);
|
||||||
|
copy.fields = fields.value;
|
||||||
|
copy.update = { fields: fields.value };
|
||||||
|
store.commit(props.pagename, copy);
|
||||||
|
showmodal.value = undefined;
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
function updateField() {
|
||||||
|
let copy = $clone(pagedata);
|
||||||
|
copy.fields = fields.value;
|
||||||
|
copy.update = { fields: fields.value };
|
||||||
|
store.commit(props.pagename, copy);
|
||||||
|
}
|
||||||
|
function moveDown(v, i) {
|
||||||
|
let idx = i === fields.value.length - 1 ? 0 : i + 1;
|
||||||
|
fields.value = $arrayMove(fields.value, i, idx);
|
||||||
|
updateField();
|
||||||
|
}
|
||||||
|
function moveUp(v, i) {
|
||||||
|
let idx = i === 0 ? fields.value.length - 1 : i - 1;
|
||||||
|
fields.value = $arrayMove(fields.value, i, idx);
|
||||||
|
updateField();
|
||||||
|
}
|
||||||
|
function askConfirm(v, i) {
|
||||||
|
current = { v: v, i: i };
|
||||||
|
showmodal.value = {
|
||||||
|
component: `dialog/Confirm`,
|
||||||
|
vbind: { content: "Bạn có muốn xóa cột này không?", duration: 10 },
|
||||||
|
title: "Xóa cột",
|
||||||
|
width: "500px",
|
||||||
|
height: "100px",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function remove() {
|
||||||
|
let arr = [current.v];
|
||||||
|
arr.map((v) => {
|
||||||
|
let idx = fields.value.findIndex((x) => x.name === v.name);
|
||||||
|
$remove(fields.value, idx);
|
||||||
|
});
|
||||||
|
updateField();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
112
components/datatable/TableSetting.vue
Normal file
112
components/datatable/TableSetting.vue
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14"> Cỡ chữ của bảng <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input class="input is-small" type="text" :value="tablesetting.find(v=>v.code==='table-font-size').detail"
|
||||||
|
@change="changeSetting($event.target.value, 'table-font-size')">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field" >
|
||||||
|
<label class="label fs-14"> Cỡ chữ tiêu đề <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input class="input is-small" type="text" :value="tablesetting.find(v=>v.code==='header-font-size').detail"
|
||||||
|
@change="changeSetting($event.target.value, 'header-font-size')">
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14"> Số dòng trên 1 trang <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input class="input is-small" type="text" :value="tablesetting.find(v=>v.code==='per-page').detail"
|
||||||
|
@change="changeSetting($event.target.value, 'per-page')">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal mt-5">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14"> Màu nền bảng <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input type="color" :value="tablesetting.find(v=>v.code==='table-background').detail"
|
||||||
|
@change="changeSetting($event.target.value, 'table-background')">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14"> Màu chữ <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input type="color" :value="tablesetting.find(v=>v.code==='table-font-color').detail"
|
||||||
|
@change="changeSetting($event.target.value, 'table-font-color')">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal mt-5">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14"> Màu chữ tiêu đề <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input type="color" :value="tablesetting.find(v=>v.code==='header-font-color').detail"
|
||||||
|
@change="changeSetting($event.target.value, 'header-font-color')">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14"> Màu nền tiêu đề <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input type="color" :value="tablesetting.find(v=>v.code==='header-background').detail"
|
||||||
|
@change="changeSetting($event.target.value, 'header-background')">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label fs-14"> Màu chữ khi filter<span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input type="color" :value="tablesetting.find(v=>v.code==='header-filter-color').detail"
|
||||||
|
@change="changeSetting($event.target.value, 'header-filter-color')">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-horizontal mt-5">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field" >
|
||||||
|
<label class="label fs-14"> Đường viền <span class="has-text-danger"> * </span> </label>
|
||||||
|
<p class="control fs-14">
|
||||||
|
<input class="input is-small" type="text"
|
||||||
|
:value="tablesetting.find(v=>v.code==='td-border')? tablesetting.find(v=>v.code==='td-border').detail : undefined"
|
||||||
|
@change="changeSetting($event.target.value, 'td-border')">
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { useStore } from '@/stores/index'
|
||||||
|
const store = useStore()
|
||||||
|
var props = defineProps({
|
||||||
|
pagename: String
|
||||||
|
})
|
||||||
|
const { $copy, $clone, $empty } = useNuxtApp()
|
||||||
|
var pagedata = $clone(store[props.pagename])
|
||||||
|
var errors = []
|
||||||
|
var radioNote = 'no'
|
||||||
|
var tablesetting = pagedata.tablesetting
|
||||||
|
let found = tablesetting.find(v=>v.code==='note')
|
||||||
|
if(found? found.detail!=='@' : false) radioNote = 'yes'
|
||||||
|
function changeSetting(value, code) {
|
||||||
|
if(code==='note' && $empty(value)) return
|
||||||
|
let copy = $copy(tablesetting)
|
||||||
|
let found = copy.find(v=>v.code===code)
|
||||||
|
if(found) found.detail = value
|
||||||
|
else {
|
||||||
|
found = $copy(tablesetting.find(v=>v.code===code))
|
||||||
|
found.detail = value
|
||||||
|
copy.push(found)
|
||||||
|
}
|
||||||
|
tablesetting = copy
|
||||||
|
pagedata.tablesetting = tablesetting
|
||||||
|
store.commit(props.pagename, pagedata)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
378
components/datatable/TimeOption.vue
Normal file
378
components/datatable/TimeOption.vue
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pb-1" style="border-bottom: 2px solid #3c5b63" v-if="array || !enableTime">
|
||||||
|
<div class="columns mx-0 mb-0">
|
||||||
|
<div class="column is-8 px-0 pb-0" v-if="enableTime">
|
||||||
|
<div class="field is-grouped is-grouped-multiline mb-0">
|
||||||
|
<div class="control mb-0">
|
||||||
|
<Caption v-bind="{ title: lang === 'vi' ? 'Thời gian' : 'Time', type: 'has-text-warning' }" />
|
||||||
|
</div>
|
||||||
|
<div class="control mb-0" v-for="v in array" :key="v.code">
|
||||||
|
<span class="icon-text fsb-16 has-text-warning px-1" v-if="v.code === current">
|
||||||
|
<SvgIcon v-bind="{ name: 'tick.png', size: 20 }"></SvgIcon>
|
||||||
|
<span>{{ v.name }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="icon-text has-text-grey hyperlink px-1 fsb-16" @click="changeOption(v)" v-else>{{
|
||||||
|
v.name
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<span v-if="newDataAvailable" class="has-text-danger is-italic is-size-6 ml-2">Có dữ liệu mới, vui lòng làm
|
||||||
|
mới.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4 px-0">
|
||||||
|
<div class="field is-grouped is-grouped-multiline mb-0">
|
||||||
|
<div class="control mb-0">
|
||||||
|
<Caption v-bind="{
|
||||||
|
type: 'has-text-warning',
|
||||||
|
title: lang === 'vi' ? `Tìm ${viewport === 1 ? '' : 'kiếm'}` : 'Search',
|
||||||
|
}" />
|
||||||
|
</div>
|
||||||
|
<div class="control mb-0">
|
||||||
|
<input class="input is-small" type="text" v-model="text"
|
||||||
|
:style="`${viewport === 1 ? 'width:150px;' : ''} border: 1px solid #BEBEBE;`" @keyup="startSearch"
|
||||||
|
id="input" :placeholder="lang === 'vi' ? 'Nhập từ khóa...' : 'Enter keyword...'" />
|
||||||
|
</div>
|
||||||
|
<div class="control mb-0">
|
||||||
|
<span class="tooltip" v-if="importdata">
|
||||||
|
<a class="mr-2" @click="openImport()">
|
||||||
|
<SvgIcon v-bind="{
|
||||||
|
name: 'upload.svg',
|
||||||
|
type: 'findata',
|
||||||
|
size: 22
|
||||||
|
}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span class="tooltiptext" style="min-width: max-content">
|
||||||
|
{{ lang === "vi" ? "Nhập dữ liệu" : "Import data" }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip" v-if="enableAdd">
|
||||||
|
<a class="mr-2" @click="$emit('add')">
|
||||||
|
<SvgIcon v-bind="{ name: 'add1.png', type: 'findata', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span class="tooltiptext" style="min-width: max-content">{{
|
||||||
|
lang === "vi" ? "Thêm mới" : "Add new"
|
||||||
|
}}</span>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip">
|
||||||
|
<a class="mr-2" @click="$emit('excel')">
|
||||||
|
<SvgIcon v-bind="{ name: 'excel.png', type: 'findata', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span class="tooltiptext" style="min-width: max-content">{{
|
||||||
|
lang === "vi" ? "Xuất excel" : "Export excel"
|
||||||
|
}}</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="tooltip">
|
||||||
|
<a @click="$emit('refresh-data')">
|
||||||
|
<SvgIcon v-bind="{ name: 'refresh.svg', type: 'findata', size: 22 }"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<span class="tooltiptext" style="min-width: max-content">{{
|
||||||
|
lang === "vi" ? "Làm mới" : "Refresh"
|
||||||
|
}}</span>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
<a class="button is-primary is-loading is-small ml-3" v-if="loading"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Modal @close="showmodal = undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useStore } from "@/stores/index"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const store = useStore()
|
||||||
|
return { store }
|
||||||
|
},
|
||||||
|
|
||||||
|
props: ["pagename", "api", "timeopt", "filter", "realtime", "newDataAvailable", "params", "importdata"],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: [
|
||||||
|
{ code: 0, name: this.lang === "vi" ? "Hôm nay" : "Today" },
|
||||||
|
{ code: 1, name: "1D" },
|
||||||
|
{ code: 7, name: "7D" },
|
||||||
|
{ code: 30, name: "1M" },
|
||||||
|
{ code: 90, name: "3M" },
|
||||||
|
{ code: 180, name: "6M" },
|
||||||
|
{ code: 360, name: "1Y" },
|
||||||
|
{ code: 36000, name: this.lang === "vi" ? "Tất cả" : "All" },
|
||||||
|
],
|
||||||
|
showmodal: undefined,
|
||||||
|
current: 7,
|
||||||
|
search: undefined,
|
||||||
|
timer: undefined,
|
||||||
|
text: undefined,
|
||||||
|
status: [{ code: 100, name: "Chọn" }],
|
||||||
|
array: undefined,
|
||||||
|
enableAdd: true,
|
||||||
|
enableTime: true,
|
||||||
|
choices: [], // Sẽ được cập nhật động từ pagedata
|
||||||
|
viewport: this.store.viewport,
|
||||||
|
pagedata: undefined,
|
||||||
|
loading: false,
|
||||||
|
pollingInterval: null,
|
||||||
|
searchableFields: [] // Lưu thông tin các field có thể tìm kiếm
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
pagename(newVal) {
|
||||||
|
this.updateSearchableFields()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async created() {
|
||||||
|
console.log("TimeOption created")
|
||||||
|
|
||||||
|
// Cập nhật searchable fields ngay từ đầu
|
||||||
|
this.updateSearchableFields()
|
||||||
|
|
||||||
|
if (this.viewport < 5) {
|
||||||
|
this.options = [
|
||||||
|
{ code: 0, name: "Hôm nay" },
|
||||||
|
{ code: 1, name: "1D" },
|
||||||
|
{ code: 7, name: "7D" },
|
||||||
|
{ code: 30, name: "1M" },
|
||||||
|
{ code: 90, name: "3M" },
|
||||||
|
{ code: 36000, name: "Tất cả" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkTimeopt()
|
||||||
|
if (!this.enableTime) return this.$emit("option")
|
||||||
|
|
||||||
|
let found = this.$findapi(this.api)
|
||||||
|
found.commit = undefined
|
||||||
|
|
||||||
|
let filter = this.$copy(this.filter)
|
||||||
|
if (filter) {
|
||||||
|
//dynamic parameter
|
||||||
|
for (const [key, value] of Object.entries(filter)) {
|
||||||
|
if (value.toString().indexOf("$") >= 0) {
|
||||||
|
filter[key] = this.store[value.replace("$", "")].id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found.params.filter) {
|
||||||
|
if (!filter) filter = {}
|
||||||
|
for (const [key, value] of Object.entries(found.params.filter)) {
|
||||||
|
filter[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.map((v) => {
|
||||||
|
let f = filter ? this.$copy(filter) : {}
|
||||||
|
f["create_time__date__gte"] = this.$dayjs()
|
||||||
|
.subtract(v.code, "day")
|
||||||
|
.format("YYYY-MM-DD")
|
||||||
|
v.filter = f
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit("option", this.$find(this.options, { code: this.current }))
|
||||||
|
|
||||||
|
let f = {}
|
||||||
|
this.options.map((v) => {
|
||||||
|
f[`${v.code}`] = {
|
||||||
|
type: "Count",
|
||||||
|
field: "create_time__date",
|
||||||
|
filter: v.filter
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let params = { summary: "aggregate", distinct_values: f }
|
||||||
|
found.params = params
|
||||||
|
|
||||||
|
try {
|
||||||
|
let rs = await this.$getapi([found])
|
||||||
|
for (const [key, value] of Object.entries(rs[0].data.rows)) {
|
||||||
|
let found = this.$find(this.options, { code: Number(key) })
|
||||||
|
if (found) {
|
||||||
|
found.name = `${found.name} (${value})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.array = this.$copy(this.options)
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if (this.realtime) {
|
||||||
|
const interval = typeof this.realtime === "number" ? this.realtime * 1000 : 5000
|
||||||
|
this.pollingInterval = setInterval(this.refresh, interval)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeUnmount() {
|
||||||
|
if (this.pollingInterval) {
|
||||||
|
clearInterval(this.pollingInterval)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
lang: function () {
|
||||||
|
return this.store.lang
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
// Lấy các field có thể tìm kiếm từ API params.values
|
||||||
|
updateSearchableFields() {
|
||||||
|
try {
|
||||||
|
// Lấy API config
|
||||||
|
const found = this.$findapi(this.api)
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
console.warn('Không tìm thấy API config')
|
||||||
|
this.choices = []
|
||||||
|
this.searchableFields = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ưu tiên lấy values từ props.params, nếu không có thì lấy từ API config
|
||||||
|
let valuesString = ''
|
||||||
|
|
||||||
|
if (this.params && this.params.values) {
|
||||||
|
// Lấy từ props.params (ưu tiên cao nhất)
|
||||||
|
valuesString = this.params.values
|
||||||
|
console.log('Using values from props.params')
|
||||||
|
} else if (found.params && found.params.values) {
|
||||||
|
// Lấy từ API config mặc định
|
||||||
|
valuesString = found.params.values
|
||||||
|
console.log('Using values from API default params')
|
||||||
|
} else {
|
||||||
|
console.warn('Không tìm thấy API values trong props hoặc config')
|
||||||
|
this.choices = []
|
||||||
|
this.searchableFields = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse values string từ API
|
||||||
|
let fieldNames = valuesString.split(',').map(v => v.trim())
|
||||||
|
|
||||||
|
console.log('Raw fieldNames:', fieldNames)
|
||||||
|
console.log('Total fields:', fieldNames.length)
|
||||||
|
|
||||||
|
// Lấy pagedata để lấy label
|
||||||
|
this.pagedata = this.store[this.pagename]
|
||||||
|
|
||||||
|
// Lấy tất cả các field để search (không lọc format)
|
||||||
|
const searchable = fieldNames.filter(fieldName => {
|
||||||
|
// Loại bỏ các field kỹ thuật
|
||||||
|
if (fieldName === 'id' ||
|
||||||
|
fieldName === 'create_time' ||
|
||||||
|
fieldName === 'update_time' ||
|
||||||
|
fieldName === 'created_at' ||
|
||||||
|
fieldName === 'updated_at') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Lấy tên và label các field
|
||||||
|
this.choices = searchable
|
||||||
|
this.searchableFields = searchable.map(fieldName => {
|
||||||
|
// Lấy field base name (trước dấu __)
|
||||||
|
const baseFieldName = fieldName.split('__')[0]
|
||||||
|
const fieldInfo = this.pagedata && this.pagedata.fields
|
||||||
|
? this.pagedata.fields.find(f => f.name === baseFieldName)
|
||||||
|
: null
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: fieldName,
|
||||||
|
label: fieldInfo ? fieldInfo.label : fieldName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Searchable fields:', this.searchableFields)
|
||||||
|
console.log('Choices:', this.choices)
|
||||||
|
console.log('Total searchable fields:', this.choices.length)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating searchable fields:', error)
|
||||||
|
this.choices = []
|
||||||
|
this.searchableFields = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
let found = this.$find(this.options, { code: this.current })
|
||||||
|
this.changeOption(found)
|
||||||
|
},
|
||||||
|
|
||||||
|
changeOption(v) {
|
||||||
|
this.current = v.code
|
||||||
|
if (this.search) {
|
||||||
|
this.text = undefined
|
||||||
|
this.search = undefined
|
||||||
|
}
|
||||||
|
this.$emit("option", this.$find(this.array, { code: this.current }))
|
||||||
|
},
|
||||||
|
|
||||||
|
doSearch() {
|
||||||
|
// Cập nhật choices trước khi search
|
||||||
|
this.updateSearchableFields()
|
||||||
|
|
||||||
|
this.pagedata = this.store[this.pagename]
|
||||||
|
|
||||||
|
if (!this.pagedata || !this.pagedata.fields) {
|
||||||
|
console.warn('Không có pagedata hoặc fields')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = this.pagedata.fields.filter(
|
||||||
|
(v) => this.choices.findIndex((x) => x === v.name) >= 0
|
||||||
|
)
|
||||||
|
|
||||||
|
if (fields.length === 0) {
|
||||||
|
console.warn('Không tìm thấy field để tìm kiếm')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let f = {}
|
||||||
|
fields.map((v) => {
|
||||||
|
f[`${v.name}__icontains`] = this.search
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Search filter:', f)
|
||||||
|
this.$emit("option", { filter_or: f })
|
||||||
|
},
|
||||||
|
|
||||||
|
openImport() {
|
||||||
|
if (!this.importdata) return
|
||||||
|
// Emit event lên parent (DataView)
|
||||||
|
this.$emit('import', this.importdata)
|
||||||
|
},
|
||||||
|
|
||||||
|
startSearch(val) {
|
||||||
|
this.search = this.$empty(val.target.value)
|
||||||
|
? ""
|
||||||
|
: val.target.value.trim()
|
||||||
|
|
||||||
|
if (this.timer) clearTimeout(this.timer)
|
||||||
|
this.timer = setTimeout(() => this.doSearch(), 300)
|
||||||
|
},
|
||||||
|
|
||||||
|
checkTimeopt() {
|
||||||
|
if (this.timeopt > 0) {
|
||||||
|
let obj = this.$find(this.options, { code: this.$formatNumber(this.timeopt) })
|
||||||
|
if (obj) this.current = obj.code
|
||||||
|
}
|
||||||
|
if (this.timeopt ? this.$empty(this.timeopt.disable) : true) return
|
||||||
|
if (this.timeopt.disable.indexOf("add") >= 0) this.enableAdd = false
|
||||||
|
if (this.timeopt.disable.indexOf("time") >= 0) this.enableTime = false
|
||||||
|
if (this.timeopt.time) {
|
||||||
|
let obj = this.$find(this.options, { code: this.$formatNumber(this.timeopt.time) })
|
||||||
|
if (obj) this.current = obj.code
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
10
components/datatable/format/ColorText.vue
Normal file
10
components/datatable/format/ColorText.vue
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<span :style="{ color: color }">{{ text }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
text: { type: String, required: true },
|
||||||
|
color: { type: String, default: "#000" }
|
||||||
|
})
|
||||||
|
</script>
|
||||||
10
components/datatable/format/FormatDate.vue
Normal file
10
components/datatable/format/FormatDate.vue
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<span :style="color? `color:${color}` : ''">{{ $dayjs(date).format('DD/MM/YYYY') }}</span>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const { $dayjs } = useNuxtApp()
|
||||||
|
const props = defineProps({
|
||||||
|
date: String,
|
||||||
|
color: String
|
||||||
|
})
|
||||||
|
</script>
|
||||||
10
components/datatable/format/FormatNumber.vue
Normal file
10
components/datatable/format/FormatNumber.vue
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<span :style="color? `color:${color}` : ''">{{ $numtoString(value) }}</span>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const { $numtoString } = useNuxtApp()
|
||||||
|
const props = defineProps({
|
||||||
|
value: Number,
|
||||||
|
color: String
|
||||||
|
})
|
||||||
|
</script>
|
||||||
10
components/datatable/format/FormatTime.vue
Normal file
10
components/datatable/format/FormatTime.vue
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<span :style="color? `color:${color}` : ''">{{ $numtoString(value) }}</span>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const { $numtoString } = useNuxtApp()
|
||||||
|
const props = defineProps({
|
||||||
|
value: Number,
|
||||||
|
color: String
|
||||||
|
})
|
||||||
|
</script>
|
||||||
130
components/datepicker/Datepicker.vue
Normal file
130
components/datepicker/Datepicker.vue
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<div class="control has-icons-left" :id="docid">
|
||||||
|
<div :class="`dropdown ${ pos || ''} ${focused? 'is-active' : ''}`" style="width: 100%;">
|
||||||
|
<div class="dropdown-trigger" style="width: 100%;">
|
||||||
|
<input :disabled="disabled" :class="`input ${error? 'is-danger' : ''} ${disabled? 'has-text-dark' : ''}`" type="text" placeholder="MM/DD/YYYY"
|
||||||
|
maxlength="10" @focus="setFocus" @blur="lostFocus" @keyup.enter="pressEnter" @keyup="checkDate" v-model="show" />
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" role="menu" @click="doClick()">
|
||||||
|
<div class="dropdown-content" style="min-width: 260px;">
|
||||||
|
<PickDay class="py-2 px-2" v-bind="{date: date}" @date="selectDate"></PickDay>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="icon is-left">
|
||||||
|
<SvgIcon v-bind="{name: 'calendar.svg', type: 'gray', size: 21}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['record', 'attr', 'position', 'mindate', 'maxdate', 'disabled'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
date: undefined,
|
||||||
|
show: undefined,
|
||||||
|
error: false,
|
||||||
|
focused: false,
|
||||||
|
docid: this.$id(),
|
||||||
|
count1: 0,
|
||||||
|
count2: 0,
|
||||||
|
pos: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getPos()
|
||||||
|
if(this.record) {
|
||||||
|
this.date = this.record[this.attr]? this.$copy(this.record[this.attr]) : undefined
|
||||||
|
if(this.date) this.show = this.$dayjs(this.date).format('DD/MM/YYYY')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
record: function(newVal) {
|
||||||
|
if(this.record) {
|
||||||
|
this.date = this.record[this.attr]? this.$copy(this.record[this.attr]) : undefined
|
||||||
|
if(this.date) this.show = this.$dayjs(this.date).format('DD/MM/YYYY')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
pressEnter() {
|
||||||
|
this.checkDate()
|
||||||
|
if(!this.error) this.focused = false
|
||||||
|
},
|
||||||
|
setFocus() {
|
||||||
|
this.focused = true
|
||||||
|
this.count1 = 0
|
||||||
|
this.count2 = 0
|
||||||
|
},
|
||||||
|
lostFocus() {
|
||||||
|
let self = this
|
||||||
|
setTimeout(()=>{
|
||||||
|
if(self.focused && self.count1===0) self.focused = false
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
|
processEvent(event) {
|
||||||
|
var doc = document.getElementById(this.docid)
|
||||||
|
if(!doc) return
|
||||||
|
this.count2 += 1
|
||||||
|
var isClickInside = false
|
||||||
|
isClickInside = doc.contains(event.target);
|
||||||
|
if(!isClickInside && this.focused) {
|
||||||
|
if(this.count2-1!==this.count1) {
|
||||||
|
this.focused = false
|
||||||
|
this.count1 = 0
|
||||||
|
this.count2 = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
doClick() {
|
||||||
|
this.count1 += 1
|
||||||
|
},
|
||||||
|
selectDate(v) {
|
||||||
|
this.date = v
|
||||||
|
this.show = this.$dayjs(v).format('DD/MM/YYYY')
|
||||||
|
this.$emit('date', this.date)
|
||||||
|
if(this.focused) this.focused = false
|
||||||
|
this.count1 = 0
|
||||||
|
this.count2 = 0
|
||||||
|
},
|
||||||
|
getDate(value) {
|
||||||
|
let v = value.replace(/\D/g,'').slice(0, 10);
|
||||||
|
if (v.length >= 5) {
|
||||||
|
return `${v.slice(0,2)}/${v.slice(2,4)}/${v.slice(4)}`;
|
||||||
|
}
|
||||||
|
else if (v.length >= 3) {
|
||||||
|
return `${v.slice(0,2)}/${v.slice(2)}`;
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
},
|
||||||
|
checkDate() {
|
||||||
|
if(!this.focused) this.setFocus()
|
||||||
|
this.error = false
|
||||||
|
this.date = undefined
|
||||||
|
if(this.$empty(this.show)) return this.$emit('date', null)
|
||||||
|
this.show = this.getDate(this.show)
|
||||||
|
let val = `${this.show.substring(6,10)}-${this.show.substring(3,5)}-${this.show.substring(0,2)}`
|
||||||
|
if(this.$dayjs(val, "YYYY-MM-DD", true).isValid()) {
|
||||||
|
this.date = val
|
||||||
|
this.$emit('date', this.date)
|
||||||
|
} else this.error = true
|
||||||
|
},
|
||||||
|
getPos() {
|
||||||
|
switch(this.position) {
|
||||||
|
case 'is-top-left':
|
||||||
|
this.pos = 'is-up is-left'
|
||||||
|
break;
|
||||||
|
case 'is-top-right':
|
||||||
|
this.pos = 'is-up is-right'
|
||||||
|
break;
|
||||||
|
case 'is-bottom-left':
|
||||||
|
this.pos = 'is-right'
|
||||||
|
break;
|
||||||
|
case 'is-bottom-right':
|
||||||
|
this.pos = 'is-right'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
185
components/datepicker/EventDetail.vue
Normal file
185
components/datepicker/EventDetail.vue
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="field is-grouped mb-4 border-bottom" v-if="1<0">
|
||||||
|
<div class="control pl-2" v-if="mode!=='simple'">
|
||||||
|
<a class="mr-1" @click="previousYear()">
|
||||||
|
<SvgIcon v-bind="{name: 'doubleleft.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a @click="previousMonth()" v-if="type==='days'">
|
||||||
|
<SvgIcon v-bind="{name: 'left1.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="control is-expanded has-text-centered">
|
||||||
|
<span class="fsb-16 hyperlink mr-3" @click="type='months'" v-if="type==='days'">{{`Tháng ${month}`}}</span>
|
||||||
|
<span class="fsb-16 hyperlink" @click="type='years'">{{ caption || year }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="control pr-2" v-if="mode!=='simple'">
|
||||||
|
<a class="mr-1" @click="nextMonth()" v-if="type==='days'">
|
||||||
|
<SvgIcon v-bind="{name: 'right.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a @click="nextYear()">
|
||||||
|
<SvgIcon v-bind="{name: 'doubleright.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="type==='days'">
|
||||||
|
<div :class="`columns mx-0 ${i===weeks.length-1? 'mb-1' : ''}`" v-for="(v,i) in weeks" :key="i">
|
||||||
|
<div class="column px-3 py-1 has-text-centered" v-for="(m,h) in v.dates" :key="h" style="min-height: 100px;"
|
||||||
|
:style="`border-right: 1px solid #DCDCDC; border-bottom: 1px solid #DCDCDC; ${(viewport>1 && h===0 || viewport===1)? 'border-left: 1px solid #DCDCDC;' : ''}
|
||||||
|
${i===0? 'border-top: 1px solid #DCDCDC;' : ''}`">
|
||||||
|
<p class="mb-1" v-if="i===0"><b>{{ $find(dateOfWeek, {id: h}).text}}</b></p>
|
||||||
|
<span class="has-background-primary has-text-white px-1 py-1" v-if="m.date===today">{{ m.dayPrint }}</span>
|
||||||
|
<span v-else>{{m.dayPrint}}</span>
|
||||||
|
<div class="has-text-left fs-15 mt-1" v-if="m.event && m.currentMonth===m.mothCondition">
|
||||||
|
<p :class="`pt-1 ${j===m.event.length-1? '' : 'border-bottom'}`" v-for="(h,j) in m.event">
|
||||||
|
<SvgIcon v-bind="{name: h.icon, type: h.color, size: 16}"></SvgIcon>
|
||||||
|
<a class="ml-3" @click="openEvent(h)">{{ h.text }}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['date', 'events', 'mode', 'vyear', 'vmonth'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dates: [],
|
||||||
|
dateOfWeek: [{id: 0, text: "CN"}, {id: 1, text: "T2"}, {id: 2, text: "T3"}, {id: 3, text: "T4"},
|
||||||
|
{id: 4, text: "T5",}, {id: 5, text: "T6"}, {id: 6, text: "T7"}],
|
||||||
|
weeks: [],
|
||||||
|
today: this.$dayjs().format('YYYY/MM/DD'),
|
||||||
|
year: undefined,
|
||||||
|
month: undefined,
|
||||||
|
type: 'days',
|
||||||
|
caption: undefined,
|
||||||
|
action: undefined,
|
||||||
|
curdate: undefined,
|
||||||
|
showmodal: undefined,
|
||||||
|
viewport: 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.showDate()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
date: function(newVal) {
|
||||||
|
if(newVal) this.showDate()
|
||||||
|
},
|
||||||
|
vmonth: function(newVal) {
|
||||||
|
this.showDate()
|
||||||
|
},
|
||||||
|
events: function(newVal) {
|
||||||
|
this.showDate()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async openEvent(event) {
|
||||||
|
let row = await this.$getdata('sale', {id: event.sale}, undefined, true)
|
||||||
|
this.showmodal = {title: 'Bán hàng', height: '500px', width: '90%', component: 'sale/Sale', vbind: {row: row, highlight: event.id}}
|
||||||
|
},
|
||||||
|
compiledComponent(value) {
|
||||||
|
return {
|
||||||
|
template: `${value}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showDate() {
|
||||||
|
this.curdate = this.date? this.date.replaceAll('-', '/') : undefined
|
||||||
|
this.year = this.$dayjs(this.curdate || this.today).year()
|
||||||
|
this.month = this.$dayjs(this.curdate || this.today).month() + 1
|
||||||
|
if(this.vyear) this.year = this.$copy(this.vyear)
|
||||||
|
if(this.vmonth) this.month = this.$copy(this.vmonth)
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
chooseToday() {
|
||||||
|
this.$emit('date', this.today.replaceAll('/', '-'))
|
||||||
|
this.year = this.$dayjs(this.today).year()
|
||||||
|
this.month = this.$dayjs(this.today).month() + 1
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
changeCaption(v) {
|
||||||
|
this.caption = v
|
||||||
|
},
|
||||||
|
selectMonth(v) {
|
||||||
|
this.month = v
|
||||||
|
this.getDates()
|
||||||
|
this.type = 'days'
|
||||||
|
},
|
||||||
|
selectYear(v) {
|
||||||
|
this.year = v
|
||||||
|
this.getDates()
|
||||||
|
this.type = 'days'
|
||||||
|
},
|
||||||
|
getDates() {
|
||||||
|
this.caption = undefined
|
||||||
|
this.dates = this.allDaysInMonth(this.year, this.month)
|
||||||
|
this.dates.map(v=>{
|
||||||
|
let event = this.events? this.$filter(this.events, {isodate: v.date}) : undefined
|
||||||
|
if(event.length>0) v.event = event
|
||||||
|
})
|
||||||
|
this.weeks = this.$unique(this.dates, ['week']).map(v=>{return {week: v.week}})
|
||||||
|
this.weeks.map(v=>{
|
||||||
|
v.dates = this.dates.filter(x=>x.week===v.week)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
nextMonth() {
|
||||||
|
let month = this.month + 1
|
||||||
|
if(month>12) {
|
||||||
|
month = 1
|
||||||
|
this.year += 1
|
||||||
|
}
|
||||||
|
this.month = month
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
previousMonth() {
|
||||||
|
let month = this.month - 1
|
||||||
|
if(month===0) {
|
||||||
|
month = 12
|
||||||
|
this.year -= 1
|
||||||
|
}
|
||||||
|
this.month = month
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
nextYear() {
|
||||||
|
if(this.type==='years') return this.action = {name: 'next', id: this.$id()}
|
||||||
|
this.year += 1
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
previousYear() {
|
||||||
|
if(this.type==='years') return this.action = {name: 'previous', id: this.$id()}
|
||||||
|
this.year -= 1
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
choose(m) {
|
||||||
|
this.$emit('date', m.date.replaceAll('/', '-'))
|
||||||
|
},
|
||||||
|
createDate(v, x, y) {
|
||||||
|
return v + '/' + (x<10? '0' + x.toString() : x.toString()) + '/' + (y<10? '0' + y.toString() : y.toString())
|
||||||
|
},
|
||||||
|
allDaysInMonth(year, month) {
|
||||||
|
let days = Array.from({length: this.$dayjs(this.createDate(year, month, 1)).daysInMonth()}, (_, i) => i + 1)
|
||||||
|
let arr = []
|
||||||
|
days.map(v=>{
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
let thedate = this.$dayjs(this.createDate(year, month, v)).weekday(i)
|
||||||
|
let date = this.$dayjs(new Date(thedate.$d)).format("YYYY/MM/DD")
|
||||||
|
let dayPrint = this.$dayjs(new Date(thedate.$d)).format("DD")
|
||||||
|
let mothCondition = this.$dayjs(date).month() +1
|
||||||
|
let currentMonth = month
|
||||||
|
let found = arr.find(x=>x.date===date)
|
||||||
|
if(!found) {
|
||||||
|
let dayOfWeek = this.$dayjs(date).day()
|
||||||
|
let week = this.$dayjs(date).week()
|
||||||
|
let ele = {date: date, week: week, day: v, dayOfWeek: dayOfWeek, dayPrint: dayPrint, mothCondition: mothCondition, currentMonth: currentMonth}
|
||||||
|
arr.push(ele)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
17
components/datepicker/EventMultiMonth.vue
Normal file
17
components/datepicker/EventMultiMonth.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="columns is-multiline mx-0">
|
||||||
|
<div class="column is-4" v-for="v in months">
|
||||||
|
<EventSummary v-bind="{events: events, mode: 'simple', vyear: v.year, vmonth: v.month}"></EventSummary>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
EventSummary: () => import('@/components/datepicker/EventSummary')
|
||||||
|
},
|
||||||
|
props: ['events', 'months']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
52
components/datepicker/EventOneMonth.vue
Normal file
52
components/datepicker/EventOneMonth.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div class="columns mx-0">
|
||||||
|
<div class="column is-narrow" v-if="1<0">
|
||||||
|
<EventSummary></EventSummary>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div class="mb-4">
|
||||||
|
<span class="fsb-17 mr-4">{{ `T${vmonth}/${vyear}` }}</span>
|
||||||
|
<a class="mr-2" @click="previous()"><SvgIcon v-bind="{name: 'left1.svg', type: 'dark', size: 18}"></SvgIcon></a>
|
||||||
|
<a class="mr-3" @click="next()"><SvgIcon v-bind="{name: 'right.svg', type: 'dark', size: 18}"></SvgIcon></a>
|
||||||
|
<a @click="refresh()"><SvgIcon v-bind="{name: 'refresh.svg', type: 'dark', size: 18}"></SvgIcon></a>
|
||||||
|
</div>
|
||||||
|
<EventDetail v-bind="{events: events, vyear: vyear, vmonth: vmonth}"></EventDetail>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import EventSummary from '@/components/datepicker/EventSummary'
|
||||||
|
import EventDetail from '@/components/datepicker/EventDetail'
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['events', 'year', 'month'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
vyear: this.year? this.$copy(this.year) : undefined,
|
||||||
|
vmonth: this.month? this.$copy(this.month) : undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
next() {
|
||||||
|
let month = this.vmonth + 1
|
||||||
|
if(month>12) {
|
||||||
|
month = 1
|
||||||
|
this.vyear += 1
|
||||||
|
}
|
||||||
|
this.vmonth = month
|
||||||
|
},
|
||||||
|
previous() {
|
||||||
|
let month = this.vmonth - 1
|
||||||
|
if(month===0) {
|
||||||
|
month = 12
|
||||||
|
this.vyear -= 1
|
||||||
|
}
|
||||||
|
this.vmonth = month
|
||||||
|
},
|
||||||
|
refresh() {
|
||||||
|
this.$emit('refresh')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
192
components/datepicker/EventSummary.vue
Normal file
192
components/datepicker/EventSummary.vue
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="field is-grouped mb-4 border-bottom">
|
||||||
|
<div class="control pl-2" v-if="mode!=='simple'">
|
||||||
|
<a class="mr-1" @click="previousYear()">
|
||||||
|
<SvgIcon v-bind="{name: 'doubleleft.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a @click="previousMonth()" v-if="type==='days'">
|
||||||
|
<SvgIcon v-bind="{name: 'left1.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="control is-expanded has-text-centered">
|
||||||
|
<span class="fsb-16 hyperlink mr-3" @click="mode==='simple'? false : type='PickMonth'" v-if="type==='days'">{{`Tháng ${month}`}}</span>
|
||||||
|
<span class="fsb-16 hyperlink" @click="mode==='simple'? false : type='PickYear'">{{ caption || year }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="control pr-2" v-if="mode!=='simple'">
|
||||||
|
<a class="mr-1" @click="nextMonth()" v-if="type==='days'">
|
||||||
|
<SvgIcon v-bind="{name: 'right.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a @click="nextYear()">
|
||||||
|
<SvgIcon v-bind="{name: 'doubleright.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="type==='days'">
|
||||||
|
<div class="columns is-mobile mx-0">
|
||||||
|
<div class="column px-2 py-1 has-text-grey-dark" v-for="(m,h) in dateOfWeek" :key="h">
|
||||||
|
{{ m.text }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="`columns is-mobile mx-0 ${i===weeks.length-1? 'mb-1' : ''}`" v-for="(v,i) in weeks" :key="i">
|
||||||
|
<div class="column px-3 py-1 fs-14" v-for="(m,h) in v.dates" :key="h">
|
||||||
|
<a v-if="m.event && m.currentMonth===m.mothCondition">
|
||||||
|
<Tooltip v-bind="{html: m.event.html, tooltip: m.event.tooltip}"></Tooltip>
|
||||||
|
</a>
|
||||||
|
<span class="has-background-findata has-text-white px-1 py-1" v-else-if="m.date===today">{{ m.dayPrint }}</span>
|
||||||
|
<span v-else>{{m.dayPrint}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template v-if="mode!=='simple'">
|
||||||
|
<div class="border-bottom"></div>
|
||||||
|
<div class="mt-2">
|
||||||
|
<span class="ml-2 mr-2">Hôm nay: </span>
|
||||||
|
<span class="has-text-primary hyperlink" @click="chooseToday()">{{ $dayjs(today).format('DD/MM/YYYY') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="type==='PickMonth'">
|
||||||
|
<PickMonth @month="selectMonth"></PickMonth>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="type==='PickYear'">
|
||||||
|
<PickYear v-bind="{year: year, month: month, action: action}" @year="selectYear" @caption="changeCaption"></PickYear>
|
||||||
|
</div>
|
||||||
|
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PickMonth: () => import('@/components/datepicker/PickMonth'),
|
||||||
|
PickYear: () => import('@/components/datepicker/PickYear')
|
||||||
|
},
|
||||||
|
props: ['date', 'events', 'mode', 'vyear', 'vmonth'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dates: [],
|
||||||
|
dateOfWeek: [{id: 0, text: "CN"}, {id: 1, text: "T2"}, {id: 2, text: "T3"}, {id: 3, text: "T4"},
|
||||||
|
{id: 4, text: "T5",}, {id: 5, text: "T6"}, {id: 6, text: "T7"}],
|
||||||
|
weeks: [],
|
||||||
|
today: this.$dayjs().format('YYYY/MM/DD'),
|
||||||
|
year: undefined,
|
||||||
|
month: undefined,
|
||||||
|
type: 'days',
|
||||||
|
caption: undefined,
|
||||||
|
action: undefined,
|
||||||
|
curdate: undefined,
|
||||||
|
showmodal: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.showDate()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
date: function(newVal) {
|
||||||
|
if(newVal) this.showDate()
|
||||||
|
},
|
||||||
|
events: function(newVal) {
|
||||||
|
this.showDate()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showDate() {
|
||||||
|
this.curdate = this.date? this.date.replaceAll('-', '/') : undefined
|
||||||
|
this.year = this.$dayjs(this.curdate || this.today).year()
|
||||||
|
this.month = this.$dayjs(this.curdate || this.today).month() + 1
|
||||||
|
if(this.vyear) this.year = this.$copy(this.vyear)
|
||||||
|
if(this.vmonth) this.month = this.$copy(this.vmonth)
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
chooseToday() {
|
||||||
|
this.$emit('date', this.today.replaceAll('/', '-'))
|
||||||
|
this.year = this.$dayjs(this.today).year()
|
||||||
|
this.month = this.$dayjs(this.today).month() + 1
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
changeCaption(v) {
|
||||||
|
this.caption = v
|
||||||
|
},
|
||||||
|
selectMonth(v) {
|
||||||
|
this.month = v
|
||||||
|
this.getDates()
|
||||||
|
this.type = 'days'
|
||||||
|
},
|
||||||
|
selectYear(v) {
|
||||||
|
this.year = v
|
||||||
|
this.getDates()
|
||||||
|
this.type = 'days'
|
||||||
|
},
|
||||||
|
getDates() {
|
||||||
|
this.caption = undefined
|
||||||
|
this.dates = this.allDaysInMonth(this.year, this.month)
|
||||||
|
this.dates.map(v=>{
|
||||||
|
let event = this.events? this.$find(this.events, {isodate: v.date}) : undefined
|
||||||
|
if(event) v.event = event
|
||||||
|
})
|
||||||
|
this.weeks = this.$unique(this.dates, ['week']).map(v=>{return {week: v.week}})
|
||||||
|
this.weeks.map(v=>{
|
||||||
|
v.dates = this.dates.filter(x=>x.week===v.week)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
nextMonth() {
|
||||||
|
let month = this.month + 1
|
||||||
|
if(month>12) {
|
||||||
|
month = 1
|
||||||
|
this.year += 1
|
||||||
|
}
|
||||||
|
this.month = month
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
previousMonth() {
|
||||||
|
let month = this.month - 1
|
||||||
|
if(month===0) {
|
||||||
|
month = 12
|
||||||
|
this.year -= 1
|
||||||
|
}
|
||||||
|
this.month = month
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
nextYear() {
|
||||||
|
if(this.type==='PickYear') return this.action = {name: 'next', id: this.$id()}
|
||||||
|
this.year += 1
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
previousYear() {
|
||||||
|
if(this.type==='PickYear') return this.action = {name: 'previous', id: this.$id()}
|
||||||
|
this.year -= 1
|
||||||
|
this.getDates()
|
||||||
|
},
|
||||||
|
choose(m) {
|
||||||
|
this.$emit('date', m.date.replaceAll('/', '-'))
|
||||||
|
},
|
||||||
|
createDate(v, x, y) {
|
||||||
|
return v + '/' + (x<10? '0' + x.toString() : x.toString()) + '/' + (y<10? '0' + y.toString() : y.toString())
|
||||||
|
},
|
||||||
|
allDaysInMonth(year, month) {
|
||||||
|
var weekday = require('dayjs/plugin/weekday')
|
||||||
|
this.$dayjs.extend(weekday)
|
||||||
|
var weekOfYear = require('dayjs/plugin/weekOfYear')
|
||||||
|
this.$dayjs.extend(weekOfYear)
|
||||||
|
let days = Array.from({length: this.$dayjs(this.createDate(year, month, 1)).daysInMonth()}, (_, i) => i + 1)
|
||||||
|
let arr = []
|
||||||
|
days.map(v=>{
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
let thedate = this.$dayjs(this.createDate(year, month, v)).weekday(i)
|
||||||
|
let date = this.$dayjs(new Date(thedate.$d)).format("YYYY/MM/DD")
|
||||||
|
let dayPrint = this.$dayjs(new Date(thedate.$d)).format("DD")
|
||||||
|
let mothCondition = this.$dayjs(date).month() +1
|
||||||
|
let currentMonth = month
|
||||||
|
let found = arr.find(x=>x.date===date)
|
||||||
|
if(!found) {
|
||||||
|
let dayOfWeek = this.$dayjs(date).day()
|
||||||
|
let week = this.$dayjs(date).week()
|
||||||
|
let ele = {date: date, week: week, day: v, dayOfWeek: dayOfWeek, dayPrint: dayPrint, mothCondition: mothCondition, currentMonth: currentMonth}
|
||||||
|
arr.push(ele)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
169
components/datepicker/PickDay.vue
Normal file
169
components/datepicker/PickDay.vue
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="field is-grouped mb-4 border-bottom">
|
||||||
|
<div class="control pl-2">
|
||||||
|
<a class="mr-1" @click="previousYear()">
|
||||||
|
<SvgIcon v-bind="{name: 'doubleleft.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a @click="previousMonth()" v-if="type==='days'">
|
||||||
|
<SvgIcon v-bind="{name: 'left1.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="control is-expanded has-text-centered">
|
||||||
|
<span class="fsb-16 mr-3" @click="type='months'" v-if="type==='days'">{{`T${month}`}}</span>
|
||||||
|
<span class="fsb-16" @click="type='years'">{{ caption || year }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="control pr-2">
|
||||||
|
<a class="mr-1" @click="nextMonth()" v-if="type==='days'">
|
||||||
|
<SvgIcon v-bind="{name: 'right.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
<a @click="nextYear()">
|
||||||
|
<SvgIcon v-bind="{name: 'doubleright.svg', type: 'gray', size: 18}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="type==='days'">
|
||||||
|
<div class="columns is-mobile mx-0">
|
||||||
|
<div class="column px-2 py-1 has-text-grey-dark" v-for="(m,h) in dateOfWeek" :key="h">
|
||||||
|
{{ m.text }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="`columns is-mobile mx-0 ${i===weeks.length-1? 'mb-1' : ''}`" v-for="(v,i) in weeks" :key="i">
|
||||||
|
<div class="column px-3 py-1" v-for="(m,h) in v.dates" :key="h">
|
||||||
|
<a class="fs-14" @click="choose(m)">
|
||||||
|
<span class="has-background-primary has-text-white px-1 py-1" v-if="m.date===curdate">{{ m.dayPrint }}</span>
|
||||||
|
<span class="has-background-findata has-text-white px-1 py-1" v-else-if="m.date===today">{{ m.dayPrint }}</span>
|
||||||
|
<span class="has-text-grey" v-else-if="m.currentMonth!==m.mothCondition">{{ m.dayPrint }}</span>
|
||||||
|
<span v-else>{{m.dayPrint}}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="border-bottom"></div>
|
||||||
|
<div class="mt-2">
|
||||||
|
<span class="ml-2 mr-2">Hôm nay: </span>
|
||||||
|
<span class="has-text-primary" @click="chooseToday()">{{ $dayjs(today).format('DD/MM/YYYY') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="type==='months'">
|
||||||
|
<PickMonth @month="selectMonth"></PickMonth>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="type==='years'">
|
||||||
|
<PickYear v-bind="{year: year, month: month, action: action}" @year="selectYear" @caption="changeCaption"></PickYear>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import PickMonth from '@/components/datepicker/PickMonth'
|
||||||
|
import PickYear from '@/components/datepicker/PickYear'
|
||||||
|
const { $id, $dayjs, $unique} = useNuxtApp()
|
||||||
|
const emit = defineEmits(['date'])
|
||||||
|
var props = defineProps({
|
||||||
|
date: String
|
||||||
|
})
|
||||||
|
var dates = []
|
||||||
|
var dateOfWeek = [{id: 0, text: "CN"}, {id: 1, text: "T2"}, {id: 2, text: "T3"}, {id: 3, text: "T4"},
|
||||||
|
{id: 4, text: "T5",}, {id: 5, text: "T6"}, {id: 6, text: "T7"}]
|
||||||
|
var weeks = ref([])
|
||||||
|
var year= undefined
|
||||||
|
var month = undefined
|
||||||
|
var type = 'days'
|
||||||
|
var caption = undefined
|
||||||
|
var action = undefined
|
||||||
|
var curdate = undefined
|
||||||
|
var today = new Date()
|
||||||
|
|
||||||
|
function showDate() {
|
||||||
|
curdate = props.date? props.date.replaceAll('-', '/') : undefined
|
||||||
|
year = $dayjs(curdate || today).year()
|
||||||
|
month = $dayjs(curdate || today).month() + 1
|
||||||
|
getDates()
|
||||||
|
}
|
||||||
|
function chooseToday() {
|
||||||
|
emit('date', today.replaceAll('/', '-'))
|
||||||
|
year = $dayjs(today).year()
|
||||||
|
month = $dayjs(today).month() + 1
|
||||||
|
getDates()
|
||||||
|
}
|
||||||
|
function changeCaption(v) {
|
||||||
|
caption = v
|
||||||
|
}
|
||||||
|
function selectMonth(v) {
|
||||||
|
month = v
|
||||||
|
getDates()
|
||||||
|
type = 'days'
|
||||||
|
}
|
||||||
|
function selectYear(v) {
|
||||||
|
year = v
|
||||||
|
getDates()
|
||||||
|
type = 'days'
|
||||||
|
}
|
||||||
|
function getDates() {
|
||||||
|
caption = undefined
|
||||||
|
dates = allDaysInMonth(year, month)
|
||||||
|
weeks.value = $unique(dates, ['week']).map(v=>{return {week: v.week}})
|
||||||
|
weeks.value.map(v=>{
|
||||||
|
v.dates = dates.filter(x=>x.week===v.week)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function nextMonth() {
|
||||||
|
month = month + 1
|
||||||
|
if(month>12) {
|
||||||
|
month = 1
|
||||||
|
year += 1
|
||||||
|
}
|
||||||
|
getDates()
|
||||||
|
}
|
||||||
|
function previousMonth() {
|
||||||
|
month = month - 1
|
||||||
|
if(month===0) {
|
||||||
|
month = 12
|
||||||
|
year -= 1
|
||||||
|
}
|
||||||
|
getDates()
|
||||||
|
}
|
||||||
|
function nextYear() {
|
||||||
|
if(type==='years') return action = {name: 'next', id: $id()}
|
||||||
|
year += 1
|
||||||
|
getDates()
|
||||||
|
}
|
||||||
|
function previousYear() {
|
||||||
|
if(type==='years') return action = {name: 'previous', id: $id()}
|
||||||
|
year -= 1
|
||||||
|
getDates()
|
||||||
|
}
|
||||||
|
function choose(m) {
|
||||||
|
emit('date', m.date.replaceAll('/', '-'))
|
||||||
|
}
|
||||||
|
function createDate(v, x, y) {
|
||||||
|
return v + '/' + (x<10? '0' + x.toString() : x.toString()) + '/' + (y<10? '0' + y.toString() : y.toString())
|
||||||
|
}
|
||||||
|
function allDaysInMonth(year, month) {
|
||||||
|
|
||||||
|
let days = Array.from({length: $dayjs(createDate(year, month, 1)).daysInMonth()}, (_, i) => i + 1)
|
||||||
|
let arr = []
|
||||||
|
days.map(v=>{
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
let thedate = $dayjs(createDate(year, month, v)).weekday(i)
|
||||||
|
let date = $dayjs(new Date(thedate.$d)).format("YYYY/MM/DD")
|
||||||
|
let dayPrint = $dayjs(new Date(thedate.$d)).format("DD")
|
||||||
|
let mothCondition = $dayjs(date).month() +1
|
||||||
|
let currentMonth = month
|
||||||
|
let found = arr.find(x=>x.date===date)
|
||||||
|
if(!found) {
|
||||||
|
let dayOfWeek = $dayjs(date).day()
|
||||||
|
let week = $dayjs(date).week()
|
||||||
|
let ele = {date: date, week: week, day: v, dayOfWeek: dayOfWeek, dayPrint: dayPrint, mothCondition: mothCondition, currentMonth: currentMonth}
|
||||||
|
arr.push(ele)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
// display
|
||||||
|
showDate()
|
||||||
|
// change date
|
||||||
|
watch(() => props.date, (newVal, oldVal) => {
|
||||||
|
showDate()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
16
components/datepicker/PickMonth.vue
Normal file
16
components/datepicker/PickMonth.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div class="columns is-mobile is-multiline mx-0">
|
||||||
|
<div class="column is-4 px-3" v-for="v in months">
|
||||||
|
<span class="hyperlink fs-14" @click="$emit('month', v)">{{ `Tháng ${v}` }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
months: [1,2,3,4,5,6,7,8,9,10,11,12]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
55
components/datepicker/PickYear.vue
Normal file
55
components/datepicker/PickYear.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="columns is-mobile is-multiline mx-0">
|
||||||
|
<div class="column is-4 has-text-centered" v-for="(v,i) in years">
|
||||||
|
<span class="hyperlink" :class="i===0 || i===11? 'has-text-grey' : ''" @click="$emit('year', v)">{{ `${v}` }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['year', 'month', 'action'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
years: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.years = [this.year]
|
||||||
|
for(let i = 1; i < 7; i++) {
|
||||||
|
this.years.push(this.year+i)
|
||||||
|
this.years.push(this.year-i)
|
||||||
|
}
|
||||||
|
this.years.sort(function(a, b) {return a - b;})
|
||||||
|
this.years = this.years.slice(0,12)
|
||||||
|
this.$emit('caption', `${this.years[1]}-${this.years[10]}`)
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
action: function(newVal) {
|
||||||
|
if(newVal.name==='next') this.next()
|
||||||
|
if(newVal.name==='previous') this.previous()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
next() {
|
||||||
|
let year = this.years[this.years.length-1]
|
||||||
|
this.years = []
|
||||||
|
for(let i = 0; i < 12; i++) {
|
||||||
|
this.years.push(year+i)
|
||||||
|
}
|
||||||
|
this.years.sort(function(a, b) {return a - b;})
|
||||||
|
this.years = this.years.slice(0,12)
|
||||||
|
this.$emit('caption', `${this.years[1]}-${this.years[10]}`)
|
||||||
|
},
|
||||||
|
previous() {
|
||||||
|
let year = this.years[0]
|
||||||
|
this.years = []
|
||||||
|
for(let i = 0; i < 12; i++) {
|
||||||
|
this.years.push(year-i)
|
||||||
|
}
|
||||||
|
this.years.sort(function(a, b) {return a - b;})
|
||||||
|
this.years = this.years.slice(0,12)
|
||||||
|
this.$emit('caption', `${this.years[1]}-${this.years[10]}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
16
components/datepicker/ViewEvent.vue
Normal file
16
components/datepicker/ViewEvent.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<span v-html="row.event.name"></span>
|
||||||
|
<!--<p v-for="v in data">{{ v.text }}</p>-->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['row'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
data: this.row.event.arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
69
components/dialog/ApprovalCode.vue
Normal file
69
components/dialog/ApprovalCode.vue
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Caption v-bind="{title: 'Nhập mã duyệt'}"></Caption>
|
||||||
|
<div class="field is-grouped mt-5">
|
||||||
|
<div class="control mr-5" v-for="v in [1,2,3,4]">
|
||||||
|
<input class="input is-dark" style="font-size: 18px; max-width: 40px; font-weight:bold; text-align: center;" type="password"
|
||||||
|
maxlength="1" :id="`input${v}`" v-model="data[v]" @keyup="changeNext(v)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<a @click="reset()">Nhập lại</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
data: {},
|
||||||
|
code: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
if(!this.approvalcode && this.$store.state.login.approval_code) this.approvalcode = this.$store.state.login.approval_code
|
||||||
|
if(!this.approvalcode) {
|
||||||
|
let user = await this.$getdata('user', {id: this.$store.state.login.id}, undefined, true)
|
||||||
|
this.approvalcode = user.approval_code
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.reset()
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
approvalcode: {
|
||||||
|
get: function() {return this.$store.state['approvalcode']},
|
||||||
|
set: function(val) {this.$store.commit('updateStore', {name: 'approvalcode', data: val})}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkError() {
|
||||||
|
if(Object.keys(this.data).length<4) return true
|
||||||
|
let code = ''
|
||||||
|
for (const [key, value] of Object.entries(this.data)) {
|
||||||
|
if(!this.$empty(value)) code += value.toString()
|
||||||
|
}
|
||||||
|
if(this.$empty(code) || !this.$isNumber(code)) return true
|
||||||
|
this.code = code
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
checkValid() {
|
||||||
|
if(this.checkError()) return this.$snackbar('Mã phê duyệt không hợp lệ')
|
||||||
|
if(this.code!==this.approvalcode) return this.$snackbar('Mã phê duyệt không chính xác')
|
||||||
|
this.$emit('modalevent', {name: 'approvalcode', data: this.code})
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
changeNext(v) {
|
||||||
|
if(this.$empty(this.data[v])) return
|
||||||
|
else if(v===4) return this.checkValid()
|
||||||
|
let doc = document.getElementById(`input${v+1}`)
|
||||||
|
if(doc) doc.focus()
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.data = {}
|
||||||
|
let doc = document.getElementById(`input1`)
|
||||||
|
if(doc) doc.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
34
components/dialog/Confirm.vue
Normal file
34
components/dialog/Confirm.vue
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-html="content"></p>
|
||||||
|
<p class="border-bottom mt-3 mb-5"></p>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<button class="button is-primary has-text-white" @click="confirm()">{{ store.lang==='en'? 'Accept' : 'Đồng ý'}} </button>
|
||||||
|
<button class="button is-dark ml-5" @click="cancel()">{{store.lang==='en'? 'Cancel' : 'Hủy bỏ'}} </button>
|
||||||
|
</div>
|
||||||
|
<div class="control" v-if="duration">
|
||||||
|
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { useStore } from '~/stores/index'
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const store = useStore()
|
||||||
|
return { store }
|
||||||
|
},
|
||||||
|
props: ['content', 'duration'],
|
||||||
|
methods: {
|
||||||
|
cancel() {
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
confirm() {
|
||||||
|
this.$emit('modalevent', {name: 'confirm'})
|
||||||
|
this.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
75
components/dialog/CountDown.vue
Normal file
75
components/dialog/CountDown.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<div id="countdown">
|
||||||
|
<div id="countdown-number"></div>
|
||||||
|
<svg><circle r="18" cx="20" cy="20" color="red"></circle></svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['duration'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
timer: undefined,
|
||||||
|
countdown: this.duration || 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
var countdownNumberEl = document.getElementById('countdown-number')
|
||||||
|
countdownNumberEl.textContent = this.countdown;
|
||||||
|
this.timer = setInterval(()=>this.startCount(), 1000)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
clearInterval(this.timer)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
startCount() {
|
||||||
|
this.countdown -= 1
|
||||||
|
var countdownNumberEl = document.getElementById('countdown-number')
|
||||||
|
countdownNumberEl.textContent = this.countdown;
|
||||||
|
if(this.countdown===0) {
|
||||||
|
clearInterval(this.timer)
|
||||||
|
this.$emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
#countdown {
|
||||||
|
position: relative;
|
||||||
|
margin: auto;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#countdown-number {
|
||||||
|
color: black;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
:deep(svg) {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
transform: rotateY(-180deg) rotateZ(-90deg);
|
||||||
|
}
|
||||||
|
:deep(svg circle) {
|
||||||
|
stroke-dasharray: 113px;
|
||||||
|
stroke-dashoffset: 0px;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-width: 2px;
|
||||||
|
stroke: black;
|
||||||
|
fill: none;
|
||||||
|
animation: countdown 10s linear infinite forwards;
|
||||||
|
}
|
||||||
|
@keyframes countdown {
|
||||||
|
from {
|
||||||
|
stroke-dashoffset: 0px;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: 113px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
47
components/dialog/Delete.vue
Normal file
47
components/dialog/Delete.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-html="content"></p>
|
||||||
|
<p class="border-bottom mt-4 mb-5"></p>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<button class="button is-danger" @click="remove()">Đồng ý</button>
|
||||||
|
<button class="button is-dark ml-5" @click="cancel()">Hủy bỏ</button>
|
||||||
|
</div>
|
||||||
|
<div class="control" v-if="duration">
|
||||||
|
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['content', 'duration', 'vbind', 'setdeleted'],
|
||||||
|
methods: {
|
||||||
|
cancel() {
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
async remove() {
|
||||||
|
let pagename = this.vbind.pagename
|
||||||
|
let pagedata = this.$store.state[pagename]
|
||||||
|
let name = pagedata.origin_api.name || this.vbind.api
|
||||||
|
let id = this.vbind.row.id
|
||||||
|
let result
|
||||||
|
if(this.setdeleted) {
|
||||||
|
let record = await this.$getdata(name, {id: id}, undefined, true)
|
||||||
|
record.deleted = 1
|
||||||
|
result = await this.$updateapi(name, record)
|
||||||
|
} else result = await this.$deleteapi(name, id)
|
||||||
|
if(result==='error') return this.$dialog('Đã xảy ra lỗi, xóa dữ liệu thất bại', 'Lỗi', 'Error')
|
||||||
|
this.$snackbar('Dữ liệu đã được xoá khỏi hệ thống', undefined, 'Success')
|
||||||
|
let arr = Array.isArray(id)? id : [{id: id}]
|
||||||
|
let copy = this.$copy(this.$store.state[pagename].data)
|
||||||
|
arr.map(x=>{
|
||||||
|
let index = copy.findIndex(v=>v.id===x.id)
|
||||||
|
index>=0? this.$delete(copy,index) : false
|
||||||
|
})
|
||||||
|
this.$store.commit('updateState', {name: pagename, key: 'update', data: {data: copy}})
|
||||||
|
this.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
28
components/dialog/Error.vue
Normal file
28
components/dialog/Error.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control is-expanded pr-3" v-html="content"></div>
|
||||||
|
<div class="control">
|
||||||
|
<SvgIcon v-bind="{name: 'error.svg', type: 'danger', size: 24}"></SvgIcon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="border-bottom mt-3 mb-5"></p>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<button class="button is-danger has-text-white" @click="cancel()">Đóng</button>
|
||||||
|
</div>
|
||||||
|
<div class="control" v-if="duration">
|
||||||
|
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
var props = defineProps({
|
||||||
|
content: String,
|
||||||
|
duration: Number
|
||||||
|
})
|
||||||
|
function cancel() {
|
||||||
|
this.$emit('close')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
24
components/dialog/Info.vue
Normal file
24
components/dialog/Info.vue
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-html="content"></p>
|
||||||
|
<p class="border-bottom mt-3 mb-5"></p>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<button class="button is-dark has-text-white" @click="cancel()">Đóng</button>
|
||||||
|
</div>
|
||||||
|
<div class="control" v-if="duration">
|
||||||
|
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['content', 'duration'],
|
||||||
|
methods: {
|
||||||
|
cancel() {
|
||||||
|
this.$emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
29
components/dialog/Success.vue
Normal file
29
components/dialog/Success.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control is-expanded pr-3" v-html="content"></div>
|
||||||
|
<div class="control">
|
||||||
|
<SvgIcon v-bind="{name: 'check2.svg', type: 'primary', size: 24}"></SvgIcon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="border-bottom mt-3 mb-5"></p>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<button class="button is-primary has-text-white" @click="cancel()">Đóng</button>
|
||||||
|
</div>
|
||||||
|
<div class="control" v-if="duration">
|
||||||
|
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['content', 'duration'],
|
||||||
|
methods: {
|
||||||
|
cancel() {
|
||||||
|
this.$emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
40
components/maintab/Configuration.vue
Normal file
40
components/maintab/Configuration.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<div class="columns mx-0">
|
||||||
|
<div class="column is-2">
|
||||||
|
<div class="py-1" v-for="v in array">
|
||||||
|
<a :class="(current? current.code===v.code : false)? 'has-text-primary has-text-weight-bold' : ''" @click="changeTab(v)">{{ v.name }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-10">
|
||||||
|
<div class="fsb-20 mb-3" v-if="current">{{ current.name }}</div>
|
||||||
|
<DataView v-bind="current.vbind" v-if="current"></DataView>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { useStore } from '~/stores/index'
|
||||||
|
const store = useStore()
|
||||||
|
const { $clone } = useNuxtApp()
|
||||||
|
|
||||||
|
var array = [
|
||||||
|
{code: 'systemsetting', name: 'System setting', vbind: {api: 'systemsetting', setting: 'common-fields-1', pagename: 'pagedata8.8.8',
|
||||||
|
timeopt: 36000,
|
||||||
|
modal: {component: 'parameter/Common', title: 'System setting', vbind: {api: 'systemsetting'}}}},
|
||||||
|
|
||||||
|
{code: 'emailsetup', name: 'Email setup', vbind: {api: 'emailsetup', setting: 'email-setup', pagename: 'pagedata8.8.8',
|
||||||
|
timeopt: 36000,
|
||||||
|
modal: {component: 'parameter/EmailSetup', title: 'Email setup', width: '600px', height: '300px', vbind: {api: 'emailsetup'}}}},
|
||||||
|
|
||||||
|
{code: 'settingfields', name: 'Setting fields',
|
||||||
|
vbind: {api: 'settingfields', setting: 'system-setting-fields', pagename: 'pagedata8.8.8', timeopt: 36000,
|
||||||
|
modal: {component: 'parameter/SettingFields', title: 'Setting fields', vbind: {api: 'settingfields'}}}}
|
||||||
|
]
|
||||||
|
var current = ref(array[0])
|
||||||
|
function changeTab(v) {
|
||||||
|
current.value = null
|
||||||
|
let copy = $clone(store['pagedata8.8.8'])
|
||||||
|
copy.filters = []
|
||||||
|
store.commit('pagedata8.8.8', copy)
|
||||||
|
setTimeout(()=>current.value = v)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
72
components/maintab/DataDeletion.vue
Normal file
72
components/maintab/DataDeletion.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div class="columns is-centered mx-0">
|
||||||
|
<div class="column is-6">
|
||||||
|
<article class="message is-danger">
|
||||||
|
<div class="message-header">
|
||||||
|
<p class="fsb-20 has-text-white">{{$lang('warning')}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="message-body has-text-black fs-17">
|
||||||
|
{{$lang('alert')}}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field is-narrow">
|
||||||
|
<label class="label">{{ $lang('confirm') }} <b class="has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" :placeholder="$lang('info')" v-model="text">
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-danger" v-if="errors.text">{{ errors.text }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ $lang('hash') }} <b class="has-text-danger">*</b></label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="secret key" v-model="password">
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-danger" v-if="errors.password">{{ errors.password }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5">
|
||||||
|
<button :class="`button is-danger ${loading? 'is-loading' : ''} has-text-white`" @click="deletion()">
|
||||||
|
Xóa dữ liệu
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const { $empty, $insertapi, $snackbar, $lang } = useNuxtApp()
|
||||||
|
var text = ref()
|
||||||
|
var password = ref()
|
||||||
|
var hash = ref()
|
||||||
|
var loading = ref(false)
|
||||||
|
var errors = ref({})
|
||||||
|
async function sha256(str) {
|
||||||
|
const buffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(str));
|
||||||
|
return Array.from(new Uint8Array(buffer)).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
}
|
||||||
|
async function checkError() {
|
||||||
|
errors.value = {}
|
||||||
|
if(text.value!=='delete') errors.value.text = "Chưa nhập xác nhận xóa"
|
||||||
|
if($empty(password.value)) errors.value.password = "Chưa nhập chuỗi bí mật"
|
||||||
|
else {
|
||||||
|
hash.value = await sha256(password.value)
|
||||||
|
if(hash.value !=='8816f9082eb2a9adf08dca6e0273d40908f409b6f8ca60bbeedd5d8d437c603b') {
|
||||||
|
errors.value.password = "Chuỗi bí mật không chính xác"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.keys(errors.value).length>0
|
||||||
|
}
|
||||||
|
async function deletion() {
|
||||||
|
let rs = await checkError()
|
||||||
|
if(rs) return
|
||||||
|
loading.value = true
|
||||||
|
rs = await $insertapi('datadeletion', {password: password.value, hash: hash.value})
|
||||||
|
if(rs==='error') $snackbar($lang('delete-error'))
|
||||||
|
else $snackbar($lang('delete-success'))
|
||||||
|
text.value = undefined
|
||||||
|
password.value = undefined
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
84
components/media/Camera.vue
Normal file
84
components/media/Camera.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div><video ref="video" id="video" width="640" height="480" autoplay></video></div>
|
||||||
|
<div class="mt-2">
|
||||||
|
<button class="button is-primary" id="snap" v-on:click="capture()">Chụp ảnh</button>
|
||||||
|
<a class="ml-6" @click="switchView()">
|
||||||
|
<SvgIcon v-bind="{name: 'camera_switch.svg', type: 'black', size: 40}"></SvgIcon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<canvas v-show="false" ref="canvas" id="canvas" width="640" height="480"></canvas>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
video: {},
|
||||||
|
canvas: {},
|
||||||
|
current: 'front'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function() {
|
||||||
|
this.openCamera()
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
var vidTrack = this.video.srcObject.getVideoTracks()
|
||||||
|
vidTrack.forEach(track => {
|
||||||
|
track.stop()
|
||||||
|
track.enabled = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openCamera() {
|
||||||
|
let f = this.current==='front'? {facingMode: "user"} : {facingMode: {exact: "environment"}}
|
||||||
|
this.video = this.$refs.video;
|
||||||
|
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||||
|
navigator.mediaDevices.getUserMedia({ video: f, audio: false }).then(stream => {
|
||||||
|
video.srcObject = stream;
|
||||||
|
this.video.play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
capture() {
|
||||||
|
this.canvas = this.$refs.canvas;
|
||||||
|
let scale = 600 / this.video.videoWidth;
|
||||||
|
let w = this.video.videoWidth * scale;
|
||||||
|
let h = this.video.videoHeight * scale;
|
||||||
|
this.canvas.width = w;
|
||||||
|
this.canvas.height = h;
|
||||||
|
var context = this.canvas
|
||||||
|
.getContext("2d")
|
||||||
|
.drawImage(this.video,0,0,w,h);
|
||||||
|
this.canvas.toBlob(blod=>this.saveAs(blod))
|
||||||
|
},
|
||||||
|
async saveAs(blod) {
|
||||||
|
var form = new FormData();
|
||||||
|
let name = `${this.$id()}.png`
|
||||||
|
this.fileName = `${this.$dayjs(new Date()).format("YYYYMMDDhhmmss")}-${name}`
|
||||||
|
form.append('filename', this.fileName)
|
||||||
|
form.append('name', name)
|
||||||
|
form.append('file', blod)
|
||||||
|
form.append('type', 'image')
|
||||||
|
form.append('size', 100)
|
||||||
|
form.append('user', this.$store.state.login.id)
|
||||||
|
let result = await this.$insertapi('upload', form)
|
||||||
|
if(result==='error') return
|
||||||
|
let row = result.rows[0]
|
||||||
|
const file = new File([blod], name, {type: "image/png"})
|
||||||
|
row.source = {file: file}
|
||||||
|
this.$emit('modalevent', {name: 'selectimage', data: row})
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
switchView() {
|
||||||
|
this.current = this.current==='front'? 'back' : 'front'
|
||||||
|
var vidTrack = this.video.srcObject.getVideoTracks()
|
||||||
|
vidTrack.forEach(track => {
|
||||||
|
track.stop()
|
||||||
|
track.enabled = false
|
||||||
|
})
|
||||||
|
this.openCamera()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
81
components/media/ChipImage.vue
Normal file
81
components/media/ChipImage.vue
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="image-container">
|
||||||
|
<a @click="open()"><img :src="image" style="max-height: 86vh !important;"></a>
|
||||||
|
<div class="image-label">
|
||||||
|
<span class="is-clickable" @click="copyImage()" v-if="display.findIndex(v=>v==='copy')>=0">
|
||||||
|
<SvgIcon v-bind="{name: 'copy.svg', type: 'primary', size: 20}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
<span class="is-clickable ml-5" @click="download()" v-if="display.findIndex(v=>v==='download')>=0">
|
||||||
|
<SvgIcon v-bind="{name: 'download.svg', type: 'primary', size: 20}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
<span class="is-clickable ml-5" @click="askConfirm()" v-if="display.findIndex(v=>v==='delete')>=0">
|
||||||
|
<SvgIcon v-bind="{name: 'bin1.svg', type: 'danger', size: 18}"></SvgIcon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Modal @close="showmodal=undefined" @remove="remove" @confirm="remove()" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { useStore } from '~/stores/index'
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const store = useStore()
|
||||||
|
return { store }
|
||||||
|
},
|
||||||
|
props: ['row', 'api', 'pagename', 'file', 'image', 'show', 'extend'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showmodal: undefined,
|
||||||
|
display: this.show || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
if(!this.file || this.extend===false) return
|
||||||
|
this.showmodal = {title: this.file.file__name, component: 'media/ChipImage',
|
||||||
|
vbind: {extend: false, show: this.show, file: this.file,
|
||||||
|
image: `${this.$getpath()}download/?name=${this.file.file__file || this.file.file}&type=file`}}
|
||||||
|
},
|
||||||
|
info() {
|
||||||
|
if(!this.file) return
|
||||||
|
this.showmodal = {vbind: {file: this.file}, component: 'media/ImageAttr',
|
||||||
|
title: 'Thông tin trích xuất từ hình ảnh', width: '35%', height: '50vh'}
|
||||||
|
},
|
||||||
|
find() {
|
||||||
|
this.showmodal = {vbind: {row: this.row, api: this.api, pagename: this.pagename}, component: 'ekyc/IDcheck',
|
||||||
|
title: 'Kiểm tra CMT / CCCD / HC / GPLX / CMT-CA / CMT-QD', width: '90%', height: '70vh'}
|
||||||
|
},
|
||||||
|
download() {
|
||||||
|
let name = this.file? this.file.file__name || this.file.name : 'download'
|
||||||
|
let path = `${this.$getpath()}download/?name=${this.file.file__file || this.file.file}&type=file`
|
||||||
|
this.$download(path, name)
|
||||||
|
},
|
||||||
|
askConfirm() {
|
||||||
|
let text = this.store.lang==='en'? 'Do you agree to delete this image' : `Bạn có đồng ý <b>xóa hình ảnh</b> này không?`
|
||||||
|
this.showmodal = {component: `dialog/Confirm`,title: this.store.lang==='en'? 'Confirm' : 'Xác nhận', width: '500px', height: '100px',
|
||||||
|
vbind: {content: text, duration: 10}}
|
||||||
|
},
|
||||||
|
remove() {
|
||||||
|
this.showmodal = undefined
|
||||||
|
this.$emit('modalevent', {name: 'remove'})
|
||||||
|
this.$emit('remove')
|
||||||
|
},
|
||||||
|
async copyImage() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.$getpath()}download/?name=${this.file.file__file || this.file.file}&type=file`);
|
||||||
|
const blob = await response.blob()
|
||||||
|
await navigator.clipboard.write([
|
||||||
|
new ClipboardItem({
|
||||||
|
[blob.type]: blob
|
||||||
|
})
|
||||||
|
])
|
||||||
|
this.$snackbar('Copied to clipboard')
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err.name, err.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
91
components/media/CropImage.vue
Normal file
91
components/media/CropImage.vue
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tile is-ancestor py-5 px-3 mx-0" v-if="image">
|
||||||
|
<div class="tile is-1"/>
|
||||||
|
<div class="tile is-7">
|
||||||
|
<Cropper
|
||||||
|
ref="cropper"
|
||||||
|
:src="image"
|
||||||
|
@change="onChange"
|
||||||
|
:stencil-props="getRatio" />
|
||||||
|
</div>
|
||||||
|
<div class="tile is-1"> </div>
|
||||||
|
<div class="tile">
|
||||||
|
<div v-if="avatar!==true">
|
||||||
|
<p class="mt-2 fs-16">Chọn tỷ lệ hoặc nhập chiều rộng và cao</p>
|
||||||
|
<div class="tags are-medium mt-2">
|
||||||
|
<a :class="curRatio.k===v.k? 'tag is-primary' : 'tag'" v-for="(v,i) in ratios" :key="i" @click="curRatio=v">{{v.k}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="block mt-5">
|
||||||
|
<b-radio v-model="radio"
|
||||||
|
native-value="replace">
|
||||||
|
Ghi đè
|
||||||
|
</b-radio>
|
||||||
|
<b-radio v-model="radio" class="ml-3"
|
||||||
|
native-value="new">
|
||||||
|
Tạo file mới
|
||||||
|
</b-radio>
|
||||||
|
</div>
|
||||||
|
<p class="mt-4">
|
||||||
|
<a class="button is-primary mr-4 mb-2" :class="loading? 'is-loading' : ''" @click="updateImage()">Lưu lại</a>
|
||||||
|
</p>
|
||||||
|
<p class="mt-2" v-if="coordinates">
|
||||||
|
Hình ảnh cắt, {{'W: ' + coordinates.width + ', H: ' + coordinates.height + ', W/H: ' + coordinates.ratio}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="is-italic has-text-grey" v-else>* Di chuyển khung để chọn hình ảnh phù hợp</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { CircleStencil, Cropper } from 'vue-advanced-cropper'
|
||||||
|
import 'vue-advanced-cropper/dist/style.css'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Cropper,
|
||||||
|
CircleStencil
|
||||||
|
},
|
||||||
|
props: ['selected', 'image', 'avatar'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
coordinates: undefined,
|
||||||
|
ratios: [{k: '1/1'}, {k: '5/4'}, {k:'4/3'}, {k: '3/2'}, {k: '5/3'}, {k:'16/9'}, {k: '2/1'}, {k: '3/1'}, {k: '4/5'}, {k: '3/4'}, {k: '2/3'}, {k: '3/5'}, {k: '9/16'}, {k: '1/2'}, {k: '1/3'}],
|
||||||
|
curRatio: {k: '1/1'},
|
||||||
|
radio: this.avatar? 'new' : 'replace',
|
||||||
|
rectangle: true,
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
getRatio() {
|
||||||
|
return {aspectRatio: this.$calc(this.curRatio.k)}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange({ coordinates, canvas}) {
|
||||||
|
this.coordinates = coordinates
|
||||||
|
this.coordinates.ratio = (this.coordinates.width*1.00 / this.coordinates.height).toFixed(2)
|
||||||
|
},
|
||||||
|
updateImage() {
|
||||||
|
const { canvas } = this.$refs.cropper.getResult()
|
||||||
|
if (canvas) canvas.toBlob(blod=>this.saveAs(blod))
|
||||||
|
},
|
||||||
|
async saveAs(blod) {
|
||||||
|
this.loading = true
|
||||||
|
var form = new FormData();
|
||||||
|
let name = this.selected.file.indexOf('-')>0? this.selected.file.substring(15, this.selected.file.length) : this.selected.file
|
||||||
|
this.fileName = this.$dayjs(new Date()).format("YYYYMMDDhhmmss") + '-' + name
|
||||||
|
if(this.radio==='replace') this.fileName = this.selected.file
|
||||||
|
form.append('filename', this.fileName)
|
||||||
|
form.append('name', name)
|
||||||
|
form.append('file', blod)
|
||||||
|
form.append('type', 'file')
|
||||||
|
form.append('size', this.selected.size)
|
||||||
|
form.append('user', this.$store.state.login.id)
|
||||||
|
let result = await this.$insertapi('upload', form)
|
||||||
|
this.loading = false
|
||||||
|
if(result==='error') return
|
||||||
|
this.$emit('modalevent', {name: 'image', data: result.rows[0]})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
32
components/media/FileCount.vue
Normal file
32
components/media/FileCount.vue
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<span class="tooltip">
|
||||||
|
<span class="dot-twitter" @click="doClick()">{{ row[attr] || 0 }}</span>
|
||||||
|
<span class="tooliptext">Hồ sơ</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['row', 'api', 'pagename', 'attr'],
|
||||||
|
methods: {
|
||||||
|
async doClick() {
|
||||||
|
let obj
|
||||||
|
if(this.attr==='image_count') {
|
||||||
|
let rs = await this.$getdata(this.api, {ref: this.row.id, file__type: 2})
|
||||||
|
if(rs.length>0) {
|
||||||
|
obj = {component: 'media/ImageShow', title: 'Hồ sơ', width: '80%', height: '70vh',
|
||||||
|
vbind: {row: this.row, image: rs.map(v=>v.file__file), pagename: this.pagename, api: this.api}}
|
||||||
|
} else {
|
||||||
|
obj = {component: 'media/ImageGallery', vbind: {row: this.row, pagename: this.pagename, api: this.api},
|
||||||
|
width: '50%', height: '500px', title: 'Hình ảnh'}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let vbind = {row: this.row, api: this.api, setting: 'media-fields', filter: {ref: this.row.id, file__type: 1}}
|
||||||
|
obj = {component: 'media/FileList', title: 'Hồ sơ', width: '50%', height: '300px', vbind: vbind}
|
||||||
|
}
|
||||||
|
this.$emit('open', {name: 'dataevent', data: {modal: obj}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user