| | |
| | | <!-- Logo --> |
| | | <div |
| | | class="header" |
| | | :class="{ 'header-dual-menu': isDualMenu }" |
| | | @click="navigateToHome" |
| | | :style="{ |
| | | background: getMenuTheme.background |
| | | }" |
| | | > |
| | | <ArtLogo v-if="!isDualMenu" class="logo" fill /> |
| | | <ArtLogo v-if="!isDualMenu" class="logo" :fill="menuOpen" :size="30" /> |
| | | <span |
| | | v-else-if="menuOpen" |
| | | class="dual-menu-system-name" |
| | | :style="{ color: getMenuTheme.systemNameColor }" |
| | | > |
| | | {{ systemName }} |
| | | </span> |
| | | </div> |
| | | <ElScrollbar :style="scrollbarStyle"> |
| | | <ElMenu |
| | |
| | | |
| | | import SidebarSubmenu from './widget/SidebarSubmenu.vue' |
| | | |
| | | import AppConfig from '@/config' |
| | | import { useSettingStore } from '@/store/modules/setting' |
| | | import { MenuTypeEnum, MenuWidth } from '@/enums/appEnum' |
| | | import { useMenuStore } from '@/store/modules/menu' |
| | |
| | | const { width } = useWindowSize() |
| | | const menuopenwidth = computed(() => getMenuOpenWidth.value) |
| | | const menuclosewidth = computed(() => MENU_CLOSE_WIDTH) |
| | | const systemName = AppConfig.systemInfo.name |
| | | const isTopLeftMenu = computed(() => menuType.value === MenuTypeEnum.TOP_LEFT) |
| | | const showLeftMenu = computed( |
| | | () => menuType.value === MenuTypeEnum.LEFT || menuType.value === MenuTypeEnum.TOP_LEFT |
| | |
| | | @use './theme'; |
| | | |
| | | .layout-sidebar { |
| | | .menu-left-open { |
| | | width: v-bind(menuopenwidth); |
| | | min-width: v-bind(menuopenwidth); |
| | | } |
| | | |
| | | .menu-left-close { |
| | | width: v-bind(menuclosewidth); |
| | | min-width: v-bind(menuclosewidth); |
| | | } |
| | | |
| | | // 展开的宽度 |
| | | .el-menu:not(.el-menu--collapse) { |
| | | width: v-bind(menuopenwidth); |
| | |
| | | display: flex; |
| | | flex-direction: column; |
| | | height: 100vh; |
| | | overflow: hidden; |
| | | transition: |
| | | width 0.25s ease, |
| | | min-width 0.25s ease; |
| | | |
| | | @media only screen and (width <= 640px) { |
| | | height: 100dvh; |
| | |
| | | height: 100%; |
| | | max-width: 126px; |
| | | max-height: 40px; |
| | | transition: |
| | | width 0.25s ease, |
| | | height 0.25s ease, |
| | | max-width 0.25s ease, |
| | | max-height 0.25s ease; |
| | | } |
| | | } |
| | | |
| | | .header-dual-menu { |
| | | justify-content: flex-start; |
| | | |
| | | .dual-menu-system-name { |
| | | display: block; |
| | | width: 100%; |
| | | overflow: hidden; |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | line-height: 1.2; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | @media only screen and (width > 800px) { |
| | | .layout-sidebar { |
| | | .menu-left-close { |
| | | .header { |
| | | padding: 10px; |
| | | |
| | | .logo { |
| | | width: 30px; |
| | | height: 30px; |
| | | max-width: 30px; |
| | | max-height: 30px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media only screen and (width <= 640px) { |
| | | .layout-sidebar { |
| | | border-right: 0 !important; |
| | |
| | | settingStore.setMenuOpen(true) |
| | | } |
| | | nextTick(() => { |
| | | settingStore.reload() |
| | | window.dispatchEvent(new Event('resize')) |
| | | }) |
| | | } |
| | | return { |
| | |
| | | } |
| | | }) |
| | | const DEFAULT_PAGINATION_OPTIONS = { |
| | | pageSizes: [10, 20, 30, 50, 100], |
| | | pageSizes: [20, 30, 50, 100], |
| | | align: 'center', |
| | | background: true, |
| | | layout: layout.value, |
| | |
| | | routeName: 'Register' |
| | | }, |
| | | { |
| | | name: '忘记密码', |
| | | enabled: true, |
| | | order: 3, |
| | | routeName: 'ForgetPassword' |
| | | }, |
| | | { |
| | | name: '个人中心', |
| | | enabled: true, |
| | | order: 4, |
| | | order: 3, |
| | | routeName: 'UserCenter' |
| | | } |
| | | ] |
| | |
| | | import { ref, reactive, computed, onMounted, onUnmounted, nextTick, readonly } from 'vue' |
| | | import { useWindowSize } from '@vueuse/core' |
| | | import { hash } from 'ohash' |
| | | import { useTableColumns } from './useTableColumns' |
| | | import { TableCache, CacheInvalidationStrategy } from '../../utils/table/tableCache' |
| | | import { |
| | |
| | | const error = ref(null) |
| | | const data = ref([]) |
| | | let abortController = null |
| | | let inflightRequest = null |
| | | let inflightRequestKey = '' |
| | | let cacheCleanupTimer = null |
| | | const searchParams = reactive( |
| | | Object.assign( |
| | | { |
| | | [pageKey]: 1, |
| | | [sizeKey]: 10 |
| | | [sizeKey]: 20 |
| | | }, |
| | | apiParams || {} |
| | | ) |
| | |
| | | cacheUpdateTrigger.value++ |
| | | } |
| | | const fetchData = async (params, useCache = enableCache) => { |
| | | if (abortController) { |
| | | abortController.abort() |
| | | } |
| | | const currentController = new AbortController() |
| | | abortController = currentController |
| | | loadingState.value = 'loading' |
| | | error.value = null |
| | | try { |
| | | let requestParams = Object.assign( |
| | | {}, |
| | | searchParams, |
| | |
| | | }) |
| | | requestParams = filteredParams |
| | | } |
| | | const requestKey = hash(requestParams) |
| | | if (inflightRequest && inflightRequestKey === requestKey) { |
| | | logger.log('复用进行中的请求') |
| | | return inflightRequest |
| | | } |
| | | if (abortController) { |
| | | abortController.abort() |
| | | } |
| | | const currentController = new AbortController() |
| | | abortController = currentController |
| | | loadingState.value = 'loading' |
| | | error.value = null |
| | | const currentRequest = (async () => { |
| | | try { |
| | | if (useCache && cache) { |
| | | const cachedItem = cache.get(requestParams) |
| | | if (cachedItem) { |
| | |
| | | if (abortController === currentController) { |
| | | abortController = null |
| | | } |
| | | if (inflightRequest === currentRequest) { |
| | | inflightRequest = null |
| | | inflightRequestKey = '' |
| | | } |
| | | } |
| | | })() |
| | | inflightRequest = currentRequest |
| | | inflightRequestKey = requestKey |
| | | return currentRequest |
| | | } |
| | | const getData = async (params) => { |
| | | try { |
| | | return await fetchData(params) |
| | |
| | | import { registerLocalIconCollections } from './plugins/iconify' |
| | | import { setupErrorHandle } from './utils/sys/error-handle' |
| | | document.addEventListener('touchstart', function () {}, { passive: false }) |
| | | registerLocalIconCollections() |
| | | |
| | | async function bootstrap() { |
| | | await registerLocalIconCollections() |
| | | |
| | | const app = createApp(App) |
| | | |
| | | initStore(app) |
| | |
| | | setupErrorHandle(app) |
| | | app.use(language) |
| | | app.mount('#app') |
| | | } |
| | | |
| | | void bootstrap() |
| | |
| | | import { addCollection } from '@iconify/vue/offline' |
| | | import { icons as fluentIcons } from '@iconify-json/fluent' |
| | | import { icons as iconParkOutlineIcons } from '@iconify-json/icon-park-outline' |
| | | import { icons as iconamoonIcons } from '@iconify-json/iconamoon' |
| | | import { icons as ixIcons } from '@iconify-json/ix' |
| | | import { icons as lineMdIcons } from '@iconify-json/line-md' |
| | | import { icons as remixIcons } from '@iconify-json/ri' |
| | | import { icons as svgSpinnersIcons } from '@iconify-json/svg-spinners' |
| | | import { icons as systemUiconsIcons } from '@iconify-json/system-uicons' |
| | | import { icons as vaadinIcons } from '@iconify-json/vaadin' |
| | | |
| | | let iconCollectionsRegistered = false |
| | | let iconCollectionsPromise = null |
| | | |
| | | export const LOCAL_ICON_COLLECTIONS = Object.freeze({ |
| | | fluent: fluentIcons, |
| | | 'icon-park-outline': iconParkOutlineIcons, |
| | | iconamoon: iconamoonIcons, |
| | | ix: ixIcons, |
| | | 'line-md': lineMdIcons, |
| | | ri: remixIcons, |
| | | 'svg-spinners': svgSpinnersIcons, |
| | | 'system-uicons': systemUiconsIcons, |
| | | vaadin: vaadinIcons |
| | | }) |
| | | |
| | | export function registerLocalIconCollections() { |
| | | export async function registerLocalIconCollections() { |
| | | if (iconCollectionsRegistered) { |
| | | return |
| | | } |
| | | |
| | | if (!iconCollectionsPromise) { |
| | | iconCollectionsPromise = import('./iconify.collections.js') |
| | | .then(({ LOCAL_ICON_COLLECTIONS }) => { |
| | | Object.values(LOCAL_ICON_COLLECTIONS).forEach((collection) => { |
| | | addCollection(collection) |
| | | }) |
| | | |
| | | iconCollectionsRegistered = true |
| | | }) |
| | | .catch((error) => { |
| | | iconCollectionsPromise = null |
| | | throw error |
| | | }) |
| | | } |
| | | |
| | | await iconCollectionsPromise |
| | | } |
| | |
| | | import { ApiStatus } from './status' |
| | | import { HttpError, handleError, showError, showSuccess } from './error' |
| | | import { $t } from '@/locales' |
| | | const REQUEST_TIMEOUT = 15e3 |
| | | const REQUEST_TIMEOUT = 30e3 |
| | | const LOGOUT_DELAY = 500 |
| | | const MAX_RETRIES = 0 |
| | | const RETRY_DELAY = 1e3 |
| | |
| | | <div class="form"> |
| | | <h3 class="title">{{ $t('login.title') }}</h3> |
| | | <p class="sub-title">{{ $t('login.subTitle') }}</p> |
| | | <div v-if="initLoading" class="mt-6"> |
| | | <ElSkeleton :rows="6" animated /> |
| | | </div> |
| | | |
| | | <ElResult |
| | | v-else-if="initErrorMessage" |
| | | class="mt-6" |
| | | icon="error" |
| | | :title="$t('crud.messages.loadFailed')" |
| | | :sub-title="initErrorMessage" |
| | | > |
| | | <template #extra> |
| | | <ElButton type="primary" @click="handleRetryInit" v-ripple> |
| | | {{ $t('common.actions.reload') }} |
| | | </ElButton> |
| | | </template> |
| | | </ElResult> |
| | | |
| | | <ElForm |
| | | v-else |
| | | ref="formRef" |
| | | :model="formData" |
| | | :rules="rules" |
| | |
| | | class="custom-height" |
| | | :placeholder="$t('login.placeholder.username')" |
| | | v-model.trim="formData.username" |
| | | :disabled="loading" |
| | | /> |
| | | </ElFormItem> |
| | | <ElFormItem prop="password"> |
| | |
| | | type="password" |
| | | autocomplete="off" |
| | | show-password |
| | | :disabled="loading" |
| | | /> |
| | | </ElFormItem> |
| | | <ElFormItem v-if="requiresTenantSelection" prop="tenantId"> |
| | |
| | | filterable |
| | | :loading="tenantLoading" |
| | | :placeholder="$t('login.placeholder.tenant')" |
| | | :disabled="loading || tenantLoading" |
| | | > |
| | | <ElOption |
| | | v-for="tenant in tenantOptions" |
| | |
| | | </ElSelect> |
| | | </ElFormItem> |
| | | |
| | | <div class="flex-cb mt-2 text-sm"> |
| | | <ElCheckbox v-model="formData.rememberPassword">{{ |
| | | <div class="mt-2 text-sm"> |
| | | <ElCheckbox v-model="formData.rememberPassword" :disabled="loading">{{ |
| | | $t('login.rememberPwd') |
| | | }}</ElCheckbox> |
| | | <RouterLink class="text-theme" :to="{ name: 'ForgetPassword' }">{{ |
| | | $t('login.forgetPwd') |
| | | }}</RouterLink> |
| | | </div> |
| | | |
| | | <div style="margin-top: 30px"> |
| | |
| | | type="primary" |
| | | @click="handleSubmit" |
| | | :loading="loading" |
| | | :disabled="loading" |
| | | v-ripple |
| | | > |
| | | {{ $t('login.btnText') }} |
| | | </ElButton> |
| | | </div> |
| | | |
| | | <!-- <div class="mt-5 text-sm text-gray-600">--> |
| | | <!-- <span>{{ $t('login.noAccount') }}</span>--> |
| | | <!-- <RouterLink class="text-theme" :to="{ name: 'Register' }">{{--> |
| | | <!-- $t('login.register')--> |
| | | <!-- }}</RouterLink>--> |
| | | <!-- </div>--> |
| | | </ElForm> |
| | | </div> |
| | | </div> |
| | |
| | | : [] |
| | | })) |
| | | const loading = ref(false) |
| | | const initLoading = ref(true) |
| | | const initErrorMessage = ref('') |
| | | |
| | | const hydrateRememberedLogin = () => { |
| | | const rememberEnabled = |
| | |
| | | } |
| | | } |
| | | |
| | | const initializeLoginPage = async () => { |
| | | initLoading.value = true |
| | | initErrorMessage.value = '' |
| | | try { |
| | | await loadLoginSupports() |
| | | await nextTick() |
| | | formRef.value?.clearValidate() |
| | | } catch (error) { |
| | | tenantOptions.value = [] |
| | | formData.tenantId = '' |
| | | initErrorMessage.value = error?.message || t('httpMsg.requestFailed') |
| | | } finally { |
| | | initLoading.value = false |
| | | } |
| | | } |
| | | |
| | | const persistRememberedLogin = () => { |
| | | localStorage.setItem( |
| | | StorageConfig.LOGIN_REMEMBER_ENABLED_KEY, |
| | |
| | | } |
| | | |
| | | const handleSubmit = async () => { |
| | | if (!formRef.value) return |
| | | if (!formRef.value || initLoading.value || initErrorMessage.value) return |
| | | try { |
| | | const valid = await formRef.value.validate() |
| | | if (!valid) return |
| | |
| | | } |
| | | } |
| | | |
| | | const handleRetryInit = () => { |
| | | void initializeLoginPage() |
| | | } |
| | | |
| | | watch( |
| | | () => formData.tenantId, |
| | | (value) => { |
| | |
| | | |
| | | onMounted(async () => { |
| | | hydrateRememberedLogin() |
| | | await loadLoginSupports() |
| | | await nextTick() |
| | | formRef.value?.clearValidate() |
| | | await initializeLoginPage() |
| | | }) |
| | | |
| | | const showLoginSuccessNotice = () => { |
| | |
| | | core: { |
| | | apiFn: fetchBasStationAreaPage, |
| | | apiParams: buildBasStationAreaPageQueryParams(searchForm.value), |
| | | immediate: false, |
| | | paginationKey: getBasStationAreaPaginationKey(), |
| | | columnsFactory: () => |
| | | createBasStationAreaTableColumns({ |
| | |
| | | core: { |
| | | apiFn: fetchDeviceBindPage, |
| | | apiParams: buildDeviceBindPageQueryParams(searchForm.value), |
| | | immediate: false, |
| | | paginationKey: getDeviceBindPaginationKey(), |
| | | columnsFactory: () => |
| | | createDeviceBindTableColumns({ |
| | |
| | | core: { |
| | | apiFn: fetchLocAreaMatRelaPage, |
| | | apiParams: buildLocAreaMatRelaPageQueryParams(searchForm.value), |
| | | immediate: false, |
| | | paginationKey: getLocAreaMatRelaPaginationKey(), |
| | | columnsFactory: () => |
| | | createLocAreaMatRelaTableColumns({ |
| | |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | await Promise.all([loadWarehouseOptions(), loadAreaOptions(), getData()]) |
| | | await Promise.all([loadWarehouseOptions(), loadAreaOptions()]) |
| | | }) |
| | | </script> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, onMounted, ref } from 'vue' |
| | | import { computed, ref } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { useUserStore } from '@/store/modules/user' |
| | | import { useAuth } from '@/hooks/core/useAuth' |
| | |
| | | resetSearchParams() |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | await getData() |
| | | }) |
| | | </script> |
| | |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | await Promise.all([loadAreaOptions(), getData()]) |
| | | await Promise.all([loadAreaOptions()]) |
| | | }) |
| | | </script> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, onMounted, ref } from 'vue' |
| | | import { computed, ref } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { useUserStore } from '@/store/modules/user' |
| | | import { useAuth } from '@/hooks/core/useAuth' |
| | |
| | | resetSearchParams() |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | await getData() |
| | | }) |
| | | </script> |
| | |
| | | core: { |
| | | apiFn: fetchTaskPathTemplateMergePage, |
| | | apiParams: buildTaskPathTemplateMergePageQueryParams(searchForm.value), |
| | | immediate: false, |
| | | paginationKey: getTaskPathTemplateMergePaginationKey(), |
| | | columnsFactory: () => |
| | | createTaskPathTemplateMergeTableColumns({ |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, onMounted, ref } from 'vue' |
| | | import { computed, ref } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { useAuth } from '@/hooks/core/useAuth' |
| | | import { useTable } from '@/hooks/core/useTable' |
| | |
| | | resetSearchParams() |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getData() |
| | | }) |
| | | </script> |
| | |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | await Promise.all([loadWarehouseOptions(), loadTypeOptions(), getData()]) |
| | | await Promise.all([loadWarehouseOptions(), loadTypeOptions()]) |
| | | }) |
| | | </script> |
| | |
| | | { timeoutMessage: t('pages.manager.locItem.messages.pageTimeout') } |
| | | ), |
| | | apiParams: buildLocItemPageQueryParams(searchForm.value), |
| | | immediate: false, |
| | | paginationKey: getLocItemPaginationKey() |
| | | }, |
| | | transform: { |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, onMounted, reactive, ref } from 'vue' |
| | | import { computed, reactive, ref } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { useUserStore } from '@/store/modules/user' |
| | | import { useTable } from '@/hooks/core/useTable' |
| | |
| | | }) |
| | | ) |
| | | |
| | | onMounted(() => { |
| | | getData() |
| | | }) |
| | | </script> |
| | |
| | | pageSize: DEFAULT_PAGE_SIZE, |
| | | orderBy: 'id desc' |
| | | }), |
| | | immediate: false, |
| | | columnsFactory: () => |
| | | createAsnOrderItemTableColumns({ |
| | | handleView: openDetail, |
| | |
| | | ...searchForm.value, |
| | | pageSize: 20 |
| | | }), |
| | | immediate: false, |
| | | columnsFactory: () => createDeliveryItemTableColumns({ handleActionClick: openDetail, t }) |
| | | }, |
| | | transform: { |
| | |
| | | ...searchForm.value, |
| | | pageSize: 20 |
| | | }), |
| | | immediate: false, |
| | | columnsFactory: () => createOutStockItemTableColumns({ handleActionClick: openDetail, t }) |
| | | }, |
| | | transform: { |
| | |
| | | core: { |
| | | apiFn: fetchPreparationItemPage, |
| | | apiParams: buildPreparationItemPageQueryParams(searchForm.value), |
| | | immediate: false, |
| | | paginationKey: getPreparationItemPaginationKey(), |
| | | columnsFactory: () => createOutStockItemTableColumns({ handleActionClick: openDetail }) |
| | | }, |
| | |
| | | core: { |
| | | apiFn: fetchPurchaseItemPage, |
| | | apiParams: buildPurchaseItemPageQueryParams(searchForm.value), |
| | | immediate: false, |
| | | paginationKey: getPurchaseItemPaginationKey(), |
| | | columnsFactory: () => |
| | | createPurchaseItemTableColumns({ |
| | |
| | | core: { |
| | | apiFn: fetchTransferItemPage, |
| | | apiParams: buildTransferItemPageQueryParams(searchForm.value), |
| | | immediate: false, |
| | | paginationKey: getTransferItemPaginationKey(), |
| | | columnsFactory: () => |
| | | createTransferItemTableColumns({ |
| | |
| | | ) |
| | | |
| | | onMounted(async () => { |
| | | await Promise.allSettled([loadTypeOptions(), loadAreaOptions(), getData()]) |
| | | await Promise.allSettled([loadTypeOptions(), loadAreaOptions()]) |
| | | }) |
| | | </script> |
| | |
| | | QueryWrapper<WkOrder> queryWrapper = pageParam.buildWrapper(true); |
| | | List<String> asList = Arrays.asList(OrderType.ORDER_IN.type); |
| | | queryWrapper.in("type", asList); |
| | | pageParam.setSearchCount(false); |
| | | return R.ok().add(asnOrderService.page(pageParam, queryWrapper)); |
| | | } |
| | | |
| | |
| | | |
| | | pressure: |
| | | asn-order: |
| | | enabled: true |
| | | enabled: false |
| | | cron: "0/10 * * * * ?" |
| | | order-count-per-run: 2000 |
| | | item-count-per-order: 10 |
| | | item-count-per-order: 1 |
| | | item-qty: 10.0 |