327 lines
11 KiB
Vue
327 lines
11 KiB
Vue
<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>
|