chore: 初始化平台管理端
This commit is contained in:
326
src/views/store/store-list/index.vue
Normal file
326
src/views/store/store-list/index.vue
Normal file
@@ -0,0 +1,326 @@
|
||||
<template>
|
||||
<div class="art-page-view">
|
||||
<StoreSearch v-model="formFilters" @search="handleSearch" @reset="handleReset" />
|
||||
|
||||
<ElCard class="art-table-card" shadow="never">
|
||||
<ArtTableHeader
|
||||
v-model:columns="columns"
|
||||
v-model:showSearchBar="showSearchBar"
|
||||
:loading="loading"
|
||||
@refresh="refreshData"
|
||||
>
|
||||
<template #actions>
|
||||
<ElButton type="primary" @click="handleCreate">
|
||||
{{ t('store.list.action.create') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ArtTableHeader>
|
||||
|
||||
<ArtTable
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@pagination:current-change="handleCurrentChange"
|
||||
@pagination:size-change="handleSizeChange"
|
||||
>
|
||||
<template #auditStatus="{ row }">
|
||||
<ElTag :type="getAuditStatusType(row.auditStatus)">
|
||||
{{ getAuditStatusText(row.auditStatus) }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<template #businessStatus="{ row }">
|
||||
<ElTag :type="getBusinessStatusType(row.businessStatus)">
|
||||
{{ getBusinessStatusText(row.businessStatus) }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<template #ownershipType="{ row }">
|
||||
<span>{{ getOwnershipTypeText(row.ownershipType) }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<div class="action-wrap">
|
||||
<button class="art-action-btn" @click="handleDetail(row)">
|
||||
<ArtSvgIcon icon="ri:information-line" class="art-action-icon" />
|
||||
<span>{{ t('dictionary.common.detail') }}</span>
|
||||
</button>
|
||||
<button class="art-action-btn primary" @click="handleEdit(row)">
|
||||
<ArtSvgIcon icon="ri:edit-2-line" class="art-action-icon" />
|
||||
<span>{{ t('store.list.action.edit') }}</span>
|
||||
</button>
|
||||
|
||||
<ElDropdown trigger="click" @command="(cmd) => handleCommand(cmd, row)">
|
||||
<button class="art-action-btn info">
|
||||
<ArtSvgIcon icon="ri:more-2-line" class="art-action-icon" />
|
||||
<span>{{ t('user.action.more') }}</span>
|
||||
</button>
|
||||
<template #dropdown>
|
||||
<ElDropdownMenu>
|
||||
<ElDropdownItem
|
||||
:disabled="row.businessStatus === StoreBusinessStatus.ForceClosed"
|
||||
command="toggleStatus"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<ArtSvgIcon icon="ri:shut-down-line" />
|
||||
<span>{{ t('store.list.action.toggleStatus') }}</span>
|
||||
</div>
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
v-if="canSubmitAudit(row)"
|
||||
:disabled="submittingId === row.id"
|
||||
command="submitAudit"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<ArtSvgIcon icon="ri:check-double-line" />
|
||||
<span>{{ t('store.list.action.submitAudit') }}</span>
|
||||
</div>
|
||||
</ElDropdownItem>
|
||||
</ElDropdownMenu>
|
||||
</template>
|
||||
</ElDropdown>
|
||||
</div>
|
||||
</template>
|
||||
</ArtTable>
|
||||
</ElCard>
|
||||
|
||||
<StoreFormDialog v-model="formDialogVisible" :store="editingStore" @saved="refreshData" />
|
||||
<BusinessStatusDialog v-model="statusDialogVisible" :store="statusStore" @saved="refreshData" />
|
||||
<StoreDetailDrawer ref="detailDrawerRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import {
|
||||
ElButton,
|
||||
ElCard,
|
||||
ElMessage,
|
||||
ElTag,
|
||||
ElDropdown,
|
||||
ElDropdownMenu,
|
||||
ElDropdownItem
|
||||
} from 'element-plus'
|
||||
import ArtTable from '@/components/core/tables/art-table/index.vue'
|
||||
import ArtTableHeader from '@/components/core/tables/art-table-header/index.vue'
|
||||
import ArtSvgIcon from '@/components/core/base/art-svg-icon/index.vue'
|
||||
import { useTable } from '@/hooks/core/useTable'
|
||||
import { fetchStoreList, fetchSubmitStoreAudit } from '@/api/store'
|
||||
import { StoreAuditStatus } from '@/enums/StoreAuditStatus'
|
||||
import { StoreBusinessStatus } from '@/enums/StoreBusinessStatus'
|
||||
import { StoreOwnershipType } from '@/enums/StoreOwnershipType'
|
||||
import { formatDateTime } from '@/utils/billing'
|
||||
import StoreFormDialog from './components/StoreFormDialog.vue'
|
||||
import BusinessStatusDialog from './components/BusinessStatusDialog.vue'
|
||||
import StoreDetailDrawer from './components/StoreDetailDrawer.vue'
|
||||
import StoreSearch from './modules/store-search.vue'
|
||||
import type { ColumnOption } from '@/types/component'
|
||||
|
||||
defineOptions({ name: 'StoreList' })
|
||||
|
||||
interface StoreListFilters {
|
||||
keyword: string
|
||||
merchantId: string
|
||||
auditStatus?: StoreAuditStatus
|
||||
businessStatus?: StoreBusinessStatus
|
||||
ownershipType?: StoreOwnershipType
|
||||
}
|
||||
|
||||
// 1. 基础状态
|
||||
const { t } = useI18n()
|
||||
const showSearchBar = ref(true)
|
||||
const formDialogVisible = ref(false)
|
||||
const statusDialogVisible = ref(false)
|
||||
const editingStore = ref<Api.Store.StoreDto | null>(null)
|
||||
const statusStore = ref<Api.Store.StoreDto | null>(null)
|
||||
const submittingId = ref<string>()
|
||||
const detailDrawerRef = ref<InstanceType<typeof StoreDetailDrawer>>()
|
||||
|
||||
// 2. 搜索筛选
|
||||
const formFilters = ref<StoreListFilters>({
|
||||
keyword: '',
|
||||
merchantId: '',
|
||||
auditStatus: undefined,
|
||||
businessStatus: undefined,
|
||||
ownershipType: undefined
|
||||
})
|
||||
|
||||
// 3. 表格配置
|
||||
const {
|
||||
data,
|
||||
loading,
|
||||
pagination,
|
||||
columns,
|
||||
searchParams,
|
||||
refreshData,
|
||||
handleCurrentChange,
|
||||
handleSizeChange
|
||||
} = useTable<typeof fetchStoreList, Api.Store.StoreDto>({
|
||||
core: {
|
||||
apiFn: fetchStoreList,
|
||||
apiParams: {
|
||||
Page: 1,
|
||||
PageSize: 20
|
||||
},
|
||||
paginationKey: {
|
||||
current: 'Page',
|
||||
size: 'PageSize'
|
||||
},
|
||||
columnsFactory: (): ColumnOption<Api.Store.StoreDto>[] => [
|
||||
{ type: 'globalIndex', label: '#', width: 60, fixed: 'left' },
|
||||
{ prop: 'name', label: t('store.list.table.name'), minWidth: 160 },
|
||||
{ prop: 'code', label: t('store.list.table.code'), minWidth: 140 },
|
||||
{
|
||||
prop: 'ownershipType',
|
||||
label: t('store.list.table.ownershipType'),
|
||||
width: 140,
|
||||
useSlot: true
|
||||
},
|
||||
{
|
||||
prop: 'auditStatus',
|
||||
label: t('store.list.table.auditStatus'),
|
||||
width: 120,
|
||||
useSlot: true
|
||||
},
|
||||
{
|
||||
prop: 'businessStatus',
|
||||
label: t('store.list.table.businessStatus'),
|
||||
width: 120,
|
||||
useSlot: true
|
||||
},
|
||||
{ prop: 'phone', label: t('store.list.table.phone'), minWidth: 140 },
|
||||
{
|
||||
prop: 'createdAt',
|
||||
label: t('store.list.table.createdAt'),
|
||||
width: 180,
|
||||
formatter: (row) => formatDateTime(row.createdAt)
|
||||
},
|
||||
{ prop: 'action', label: t('common.action'), width: 280, fixed: 'right', useSlot: true }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 5. 搜索与操作
|
||||
const handleSearch = () => {
|
||||
searchParams.Keyword = formFilters.value.keyword.trim() || undefined
|
||||
searchParams.MerchantId = formFilters.value.merchantId.trim() || undefined
|
||||
searchParams.AuditStatus = formFilters.value.auditStatus
|
||||
searchParams.BusinessStatus = formFilters.value.businessStatus
|
||||
searchParams.OwnershipType = formFilters.value.ownershipType
|
||||
refreshData()
|
||||
}
|
||||
const handleReset = () => {
|
||||
formFilters.value = {
|
||||
keyword: '',
|
||||
merchantId: '',
|
||||
auditStatus: undefined,
|
||||
businessStatus: undefined,
|
||||
ownershipType: undefined
|
||||
}
|
||||
searchParams.Keyword = undefined
|
||||
searchParams.MerchantId = undefined
|
||||
searchParams.AuditStatus = undefined
|
||||
searchParams.BusinessStatus = undefined
|
||||
searchParams.OwnershipType = undefined
|
||||
refreshData()
|
||||
}
|
||||
const handleCreate = () => {
|
||||
editingStore.value = null
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
const handleEdit = (row: Api.Store.StoreDto) => {
|
||||
editingStore.value = row
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
const handleToggleStatus = (row: Api.Store.StoreDto) => {
|
||||
statusStore.value = row
|
||||
statusDialogVisible.value = true
|
||||
}
|
||||
const handleDetail = (row: Api.Store.StoreDto) => {
|
||||
detailDrawerRef.value?.open(row.id)
|
||||
}
|
||||
|
||||
const handleCommand = (command: string | number | object, row: Api.Store.StoreDto) => {
|
||||
if (command === 'toggleStatus') {
|
||||
handleToggleStatus(row)
|
||||
} else if (command === 'submitAudit') {
|
||||
handleSubmitAudit(row)
|
||||
}
|
||||
}
|
||||
|
||||
const canSubmitAudit = (row: Api.Store.StoreDto) =>
|
||||
row.ownershipType === StoreOwnershipType.DifferentEntity &&
|
||||
[StoreAuditStatus.Draft, StoreAuditStatus.Rejected].includes(row.auditStatus)
|
||||
const handleSubmitAudit = async (row: Api.Store.StoreDto) => {
|
||||
submittingId.value = row.id
|
||||
try {
|
||||
await fetchSubmitStoreAudit(row.id)
|
||||
ElMessage.success(t('store.message.submitAuditSuccess'))
|
||||
} finally {
|
||||
submittingId.value = undefined
|
||||
}
|
||||
refreshData()
|
||||
}
|
||||
// 6. 状态显示
|
||||
const getAuditStatusType = (status: StoreAuditStatus) => {
|
||||
switch (status) {
|
||||
case StoreAuditStatus.Draft:
|
||||
return 'info'
|
||||
case StoreAuditStatus.Pending:
|
||||
return 'warning'
|
||||
case StoreAuditStatus.Activated:
|
||||
return 'success'
|
||||
case StoreAuditStatus.Rejected:
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
}
|
||||
const getAuditStatusText = (status: StoreAuditStatus) => {
|
||||
switch (status) {
|
||||
case StoreAuditStatus.Draft:
|
||||
return t('store.auditStatus.draft')
|
||||
case StoreAuditStatus.Pending:
|
||||
return t('store.auditStatus.pending')
|
||||
case StoreAuditStatus.Activated:
|
||||
return t('store.auditStatus.activated')
|
||||
case StoreAuditStatus.Rejected:
|
||||
return t('store.auditStatus.rejected')
|
||||
default:
|
||||
return t('store.auditStatus.unknown')
|
||||
}
|
||||
}
|
||||
const getBusinessStatusType = (status: StoreBusinessStatus) => {
|
||||
switch (status) {
|
||||
case StoreBusinessStatus.Open:
|
||||
return 'success'
|
||||
case StoreBusinessStatus.Resting:
|
||||
return 'warning'
|
||||
case StoreBusinessStatus.ForceClosed:
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
}
|
||||
const getBusinessStatusText = (status: StoreBusinessStatus) => {
|
||||
switch (status) {
|
||||
case StoreBusinessStatus.Open:
|
||||
return t('store.businessStatus.open')
|
||||
case StoreBusinessStatus.Resting:
|
||||
return t('store.businessStatus.resting')
|
||||
case StoreBusinessStatus.ForceClosed:
|
||||
return t('store.businessStatus.forceClosed')
|
||||
default:
|
||||
return t('store.businessStatus.unknown')
|
||||
}
|
||||
}
|
||||
const getOwnershipTypeText = (type?: StoreOwnershipType) => {
|
||||
switch (type) {
|
||||
case StoreOwnershipType.SameEntity:
|
||||
return t('store.ownership.same')
|
||||
case StoreOwnershipType.DifferentEntity:
|
||||
return t('store.ownership.different')
|
||||
default:
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user