| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- <template>
- <div class="main-wrap p-4 flex flex-col gap-y-4">
- <NCard
- ref="mainCardRef"
- class="h-full"
- content-class="h-full flex flex-col"
- :style="cardHeight"
- >
- <div class="flex gap-2 min-h-6 items-center">
- <NForm inline label-placement="left" label-width="auto" :show-feedback="false" class="flex-1 flex-wrap">
- <NFormItem label="Status">
- <NSelect
- v-model:value="selectStatus"
- class="w-180px"
- :options="optionsStatus"
- multiple
- clearable
- :max-tag-count="2"
- />
- </NFormItem>
- <NFormItem label="Platform">
- <NSelect
- v-model:value="selectPlatform"
- class="w-160px"
- :options="optionsPlatform"
- multiple
- clearable
- :max-tag-count="1"
- />
- </NFormItem>
- <NFormItem label="GICCID">
- <NInput
- v-model:value="searchKeyword"
- round
- clearable
- placeholder="搜索..."
- style="max-width: 200px"
- size="small"
- />
- </NFormItem>
- <NFormItem>
- <NButton text :focusable="false" style="font-size: 18px" @click="clickSearch">
- <span class="i-mdi:search" />
- </NButton>
- </NFormItem>
- </NForm>
- <NButton
- class="shrink-0"
- text
- :focusable="false"
- style="font-size: 18px"
- disabled
- @click="clickExportSiteInfo"
- >
- <span class="i-material-symbols:download-rounded" />
- </NButton>
- </div>
- <NDivider style="margin: 16px 0" />
- <div class="flex flex-1 flex-col min-h-0">
- <div ref="tableWrapper" class="flex-1 min-h-0 overflow-hidden">
- <NDataTable
- ref="dataTableRef"
- v-model:checked-row-keys="checkedRowKeys"
- class="max-height-auto"
- :remote="true"
- size="small"
- :columns="tableColumns"
- :data="tableData"
- :row-key="tableRowKey"
- :loading="isRequestLoading"
- striped
- :single-line="false"
- :style="{ height: `${tableHeight}px` }"
- flex-height
- :scroll-x="dataTableMaxWidth"
- />
- </div>
- <div class="mt-3 flex items-end justify-between">
- <div class="flex gap-x-3 items-center">
- <span>已选择 {{ checkedRowKeys.length }} 条</span>
- </div>
- <NPagination v-bind="tablePagination" :prefix="paginationPrefix" />
- </div>
- </div>
- </NCard>
- <SimCardDataView v-model:show="showSimCardDataView" :data="simCardData" />
- <SiteInfoView ref="siteInfoViewRef" />
- </div>
- </template>
- <script setup lang="tsx">
- import { useClipboard } from '@vueuse/core'
- import {
- useDialog,
- useMessage,
- NCard,
- NButton,
- NInput,
- NDivider,
- NDataTable,
- NNumberAnimation,
- NSelect,
- NForm,
- NFormItem,
- NScrollbar,
- NSpin,
- } from 'naive-ui'
- import type { PaginationProps, DataTableColumns, SelectOption } from 'naive-ui'
- import { useFullCard } from '@/composable/comp/useFullCard'
- import { getSimCardInfo } from '@/service/simCardManage'
- import type {
- SimCardInfo,
- SimCardMonthlyData,
- SimCardPlatform,
- SimCardStatus,
- } from '@/service/simCardManage/interface'
- import SimCardDataView from './component/simCardDataView.vue'
- import SiteInfoView from '../site-info-manage/component/siteInfoView.vue'
- import { useSimCardMonthlyCache, cacheKey } from './useSimCardMonthlyCache'
- const dialog = useDialog()
- const message = useMessage()
- const { memoryCache, loadingSet, failedSet, computeDailyIncrement, onPageChange, getWithPriority } =
- useSimCardMonthlyCache()
- const mainCardRef = useTemplateRef<InstanceType<typeof NCard>>('mainCardRef')
- const dataTableRef = useTemplateRef<InstanceType<typeof NDataTable>>('dataTableRef')
- const siteInfoViewRef = useTemplateRef<InstanceType<typeof SiteInfoView>>('siteInfoViewRef')
- const { height } = useFullCard(mainCardRef)
- const searchKeyword = ref('')
- const clickSearch = async () => {
- // 请求查询数据
- handleListQuery()
- }
- const isRequestLoading = ref(false)
- const checkedRowKeys = ref<Array<number | string>>([])
- const tableWrapper = ref<HTMLDivElement | null>(null)
- const tableHeight = ref(100)
- const cardHeight = computed(() => {
- return { height: `${height.value === undefined || height.value === 0 ? 480 : height.value}px` }
- })
- const prevUserListTotal = ref(0)
- const paginationPrefix: PaginationProps['prefix'] = (info) => {
- const { itemCount } = info
- return (
- itemCount && (
- <div>
- <span>总 </span>
- <NNumberAnimation
- from={prevUserListTotal.value}
- to={itemCount}
- onFinish={() => {
- prevUserListTotal.value = itemCount
- }}
- />
- <span> 条 </span>
- </div>
- )
- )
- }
- const tableRowKey = (rowData: SimCardInfo) => {
- return rowData.giccid
- }
- const showSimCardDataView = ref<boolean>(false)
- const simCardData = ref<SimCardMonthlyData>({
- simId: '',
- updateTime: '',
- month: '',
- allDataUsage: 0,
- dailyUsage: {},
- })
- function createColumns(): DataTableColumns<SimCardInfo> {
- return [
- {
- title: 'No.',
- key: '',
- width: 50,
- minWidth: 50,
- fixed: 'left',
- render(_, index) {
- const page = tablePagination.page ?? 1
- const pageSize = tablePagination.pageSize ?? 10
- const n = (page - 1) * pageSize + index + 1
- return `${n}`
- },
- },
- {
- title: 'GICCID',
- key: 'giccid',
- width: 200,
- fixed: 'left',
- render(rowData, rowIndex) {
- return h(
- 'div',
- { class: 'flex gap-1' },
- {
- default: () => [
- h(
- NButton,
- {
- text: true,
- onClick: () => {
- getWithPriority(rowData.giccid, rowData.month)
- .then((resp) => {
- simCardData.value = resp
- showSimCardDataView.value = true
- })
- .catch((err: unknown) => {
- if (err instanceof Error) {
- message.error(err.message)
- } else {
- message.error(String(err))
- }
- })
- },
- },
- { default: () => rowData.giccid },
- ),
- h(
- NButton,
- {
- text: true,
- focusable: false,
- onClick: () => {
- useClipboard()
- .copy(rowData.giccid)
- .then(() => {
- message.success('复制成功')
- })
- .catch(() => {
- message.error('复制失败')
- })
- },
- },
- {
- default: () => h('span', { class: 'i-ph:copy-duotone' }),
- },
- ),
- ],
- },
- )
- },
- },
- {
- title: 'SN',
- key: 'sn',
- width: 200,
- render(rowData, rowIndex) {
- return h(
- NScrollbar,
- {
- style: 'max-height: 120px',
- trigger: 'none',
- },
- {
- // default: () => rowData.sn.join(',\n'),
- default: () => [
- ...rowData.sn.map((sn) => {
- return h(
- NButton,
- {
- text: true,
- onClick: () => siteInfoViewRef.value?.show(sn),
- },
- {
- default: () => sn,
- },
- )
- }),
- ],
- },
- )
- },
- },
- {
- title: 'Status',
- key: 'status',
- width: 150,
- },
- {
- title: 'Platform',
- key: 'platform',
- width: 120,
- },
- {
- title: 'Usage (MB)',
- key: 'allDataUsage',
- minWidth: 100,
- render(rowData) {
- return (rowData.allDataUsage / 1024).toFixed(2)
- },
- },
- {
- title: 'Increment (MB)',
- key: 'increment',
- minWidth: 130,
- render(rowData) {
- const key = cacheKey(rowData.giccid, rowData.month)
- // 请求失败:停止转圈,显示 "-"
- if (failedSet.has(key)) return '-'
- // 正在加载 或 尚未开始加载:显示加载态
- if (loadingSet.has(key) || !memoryCache.has(key)) {
- return h(NSpin, { size: 'small' })
- }
- const increment = computeDailyIncrement(rowData.giccid, rowData.month)
- if (increment === null) return '-'
- const valueMB = (increment / 1024).toFixed(2)
- return increment >= 0 ? `+${valueMB}` : valueMB
- },
- },
- {
- title: 'UpdateTime',
- key: 'updateTime',
- width: 300,
- },
- ]
- }
- const selectStatus = ref<SimCardStatus[]>([])
- const optionsStatus: SelectOption[] = [
- { label: 'UNKNOWN', value: 'UNKNOWN' },
- { label: 'NOT_ACTIVATED', value: 'NOT_ACTIVATED' },
- { label: 'ACTIVATED', value: 'ACTIVATED' },
- { label: 'DEACTIVATED', value: 'DEACTIVATED' },
- { label: 'RETIRED', value: 'RETIRED' },
- ]
- const selectPlatform = ref<SimCardPlatform[]>([])
- const optionsPlatform: SelectOption[] = [
- { label: 'SmartHub', value: 'SmartHub' },
- { label: 'Acceleronix', value: 'Acceleronix' },
- ]
- const tableData = ref<SimCardInfo[]>([])
- const dataTableMaxWidth = ref<number | undefined>(1250)
- const tableColumns = computed<DataTableColumns<SimCardInfo>>(createColumns)
- const tablePagination = reactive<PaginationProps>({
- simple: false,
- page: 1,
- pageSize: 10,
- showSizePicker: true,
- pageSizes: [10, 20, 50, 100],
- pageSlot: 5,
- itemCount: 0,
- showQuickJumper: false,
- showQuickJumpDropdown: true,
- onUpdatePage: (page: number) => {
- tablePagination.page = page
- handleListQuery()
- },
- onUpdatePageSize: (pageSize: number) => {
- tablePagination.pageSize = pageSize
- tablePagination.page = 1
- handleListQuery()
- },
- })
- const clickExportSiteInfo = () => {
- dialog.warning({
- title: '确认导出',
- content: `是否确定导出当前查询条件的${tablePagination.itemCount}条数据?`,
- positiveText: '确定',
- negativeText: '取消',
- maskClosable: false,
- onPositiveClick: () => {
- console.log('开始导出')
- },
- onNegativeClick: () => {
- console.log('取消导出')
- },
- })
- }
- const querySimCardInfoList = async (
- pageSize: number,
- pageNum: number,
- search: string,
- status: string | undefined,
- platform: string | undefined,
- ) => {
- const responseList = await getSimCardInfo(pageNum, pageSize, search, status, platform)
- if (responseList.data != null) {
- tableData.value = responseList.data
- } else {
- tableData.value = []
- }
- tablePagination.itemCount = responseList.total
- // 触发预加载队列(已缓存的会被跳过)
- if (tableData.value.length > 0) {
- onPageChange(tableData.value)
- }
- }
- const handleListQuery = async () => {
- const pageSize = tablePagination.pageSize ?? 10
- const pageNum = tablePagination.page ?? 1
- const status = selectStatus.value.length > 0 ? selectStatus.value.join(',') : undefined
- const platform = selectPlatform.value.length > 0 ? selectPlatform.value.join(',') : undefined
- await querySimCardInfoList(pageSize, pageNum, searchKeyword.value, status, platform)
- }
- onMounted(async () => {
- // 请求查询数据
- await handleListQuery()
- nextTick(() => {
- const observer = new ResizeObserver((entries) => {
- tableHeight.value = entries[0].contentRect.height
- })
- if (tableWrapper.value) {
- observer.observe(tableWrapper.value)
- }
- })
- })
- </script>
|