fix(web-admin): restore category parent tree-select and delete tip.
Keep Vant sheet/form shell while fixing parent UX and creator-delete feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
5d943cf76b
commit
3c154d69ee
1 file changed
+71
-85
@@ -17,7 +17,10 @@
|
||||
<div v-if="loading && !tree.length" class="mp-product-loading">
|
||||
<van-loading vertical>正在加载分类</van-loading>
|
||||
</div>
|
||||
<van-empty v-else-if="!flatNodes.length" description="暂无分类,点击右上角新增">
|
||||
<van-empty
|
||||
v-else-if="!flatNodes.length"
|
||||
description="暂无分类,点击右上角「新增」创建顶级分类"
|
||||
>
|
||||
<van-button round type="primary" @click="openAdd()">新增分类</van-button>
|
||||
</van-empty>
|
||||
<div v-else class="mp-cat-tree">
|
||||
@@ -77,6 +80,7 @@
|
||||
v-model:show="actionOpen"
|
||||
teleport="body"
|
||||
:actions="actionSheetActions"
|
||||
:description="actionSheetDescription"
|
||||
cancel-text="取消"
|
||||
close-on-click-action
|
||||
@select="onActionSelect"
|
||||
@@ -101,14 +105,23 @@
|
||||
/>
|
||||
<div class="mp-customer-pool-vant__body">
|
||||
<van-cell-group inset>
|
||||
<van-field
|
||||
is-link
|
||||
readonly
|
||||
label="上级分类"
|
||||
:model-value="parentLabel"
|
||||
placeholder="选择上级分类(留空为顶级)"
|
||||
@click="parentPickerOpen = true"
|
||||
/>
|
||||
<van-field label="上级分类" label-align="top">
|
||||
<template #input>
|
||||
<el-tree-select
|
||||
v-model="formData.parentId"
|
||||
:data="categoryTreeOptions"
|
||||
node-key="id"
|
||||
:props="{ label: 'categoryName', children: 'children' }"
|
||||
value-key="id"
|
||||
check-strictly
|
||||
filterable
|
||||
clearable
|
||||
placeholder="选择上级分类(留空为顶级)"
|
||||
style="width: 100%"
|
||||
teleported
|
||||
/>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field
|
||||
v-model="formData.categoryName"
|
||||
label="分类名称"
|
||||
@@ -122,14 +135,23 @@
|
||||
label="排序号"
|
||||
placeholder="数值越小越靠前"
|
||||
/>
|
||||
<van-field
|
||||
is-link
|
||||
readonly
|
||||
label="状态"
|
||||
:model-value="statusLabel"
|
||||
placeholder="请选择状态"
|
||||
@click="statusPickerOpen = true"
|
||||
/>
|
||||
<van-field label="状态" label-align="top">
|
||||
<template #input>
|
||||
<el-select
|
||||
v-model="formData.status"
|
||||
placeholder="请选择状态"
|
||||
style="width: 100%"
|
||||
teleported
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in dictStore.getOptions('sys_normal_disable')"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field
|
||||
v-model="formData.remark"
|
||||
rows="3"
|
||||
@@ -148,24 +170,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model:show="parentPickerOpen" position="bottom" round teleport="body">
|
||||
<van-picker
|
||||
:columns="parentPickerColumns"
|
||||
:model-value="[formData.parentId || '0']"
|
||||
@confirm="onParentConfirm"
|
||||
@cancel="parentPickerOpen = false"
|
||||
/>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model:show="statusPickerOpen" position="bottom" round teleport="body">
|
||||
<van-picker
|
||||
:columns="statusPickerColumns"
|
||||
:model-value="[formData.status || '0']"
|
||||
@confirm="onStatusConfirm"
|
||||
@cancel="statusPickerOpen = false"
|
||||
/>
|
||||
</van-popup>
|
||||
</div>
|
||||
</MpWebviewShell>
|
||||
</template>
|
||||
@@ -224,9 +228,7 @@
|
||||
const formTitle = ref('')
|
||||
const submitLoading = ref(false)
|
||||
const nameError = ref('')
|
||||
const parentPickerOpen = ref(false)
|
||||
const statusPickerOpen = ref(false)
|
||||
const flatParentOptions = ref<Array<{ text: string; value: string }>>([])
|
||||
const categoryTreeOptions = ref<CategoryNode[]>([])
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
parentId: '' as string,
|
||||
@@ -247,6 +249,10 @@
|
||||
canDeleteByCreator(actionNode.value as Record<string, unknown> | null)
|
||||
)
|
||||
|
||||
const actionSheetDescription = computed(() =>
|
||||
canDeleteActionNode.value ? undefined : CREATOR_DELETE_TIP
|
||||
)
|
||||
|
||||
const actionSheetActions = computed(() => [
|
||||
{ name: '新增子级', value: 'add' },
|
||||
{ name: '编辑', value: 'edit' },
|
||||
@@ -258,28 +264,6 @@
|
||||
}
|
||||
])
|
||||
|
||||
const parentPickerColumns = computed(() => [
|
||||
{ text: '顶级分类', value: '0' },
|
||||
...flatParentOptions.value
|
||||
])
|
||||
|
||||
const statusPickerColumns = computed(() =>
|
||||
dictStore.getOptions('sys_normal_disable').map((o) => ({
|
||||
text: o.label,
|
||||
value: String(o.value)
|
||||
}))
|
||||
)
|
||||
|
||||
const parentLabel = computed(() => {
|
||||
const id = formData.parentId || '0'
|
||||
if (id === '0' || !id) return '顶级分类'
|
||||
return parentPickerColumns.value.find((c) => c.value === id)?.text || id
|
||||
})
|
||||
|
||||
const statusLabel = computed(() => {
|
||||
return dictStore.getLabel('sys_normal_disable', formData.status) || ''
|
||||
})
|
||||
|
||||
const flatNodes = computed((): FlatItem[] => {
|
||||
const out: FlatItem[] = []
|
||||
const walk = (nodes: CategoryNode[], depth: number) => {
|
||||
@@ -300,14 +284,6 @@
|
||||
return 'success'
|
||||
}
|
||||
|
||||
function flattenParents(nodes: CategoryNode[], parents: string[] = []): Array<{ text: string; value: string }> {
|
||||
return nodes.flatMap((node) => {
|
||||
const path = [...parents, String(node.categoryName || '')].filter(Boolean)
|
||||
const current = node.id ? [{ text: path.join(' / '), value: String(node.id) }] : []
|
||||
return current.concat(flattenParents(node.children || [], path))
|
||||
})
|
||||
}
|
||||
|
||||
const expandAllLabel = computed(() =>
|
||||
areAllExpandableExpanded(tree.value, expandedIds.value) ? '全部折叠' : '全部展开'
|
||||
)
|
||||
@@ -332,10 +308,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshParentOptions() {
|
||||
async function getCategoryTreeSelect() {
|
||||
const res = await fetchCategoryTree({})
|
||||
const data = ((res as { data?: CategoryNode[] }).data || res || []) as CategoryNode[]
|
||||
flatParentOptions.value = flattenParents(Array.isArray(data) ? data : [])
|
||||
categoryTreeOptions.value = [
|
||||
{
|
||||
id: '0',
|
||||
categoryName: '顶级分类',
|
||||
parentId: '0',
|
||||
children: Array.isArray(data) ? data : []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function toggleExpand(id: string) {
|
||||
@@ -366,7 +349,7 @@
|
||||
async function openAdd(parentRow?: CategoryNode) {
|
||||
actionOpen.value = false
|
||||
resetForm()
|
||||
await refreshParentOptions()
|
||||
await getCategoryTreeSelect()
|
||||
if (parentRow?.id) formData.parentId = parentRow.id
|
||||
formTitle.value = '新增产品分类'
|
||||
formOpen.value = true
|
||||
@@ -375,7 +358,7 @@
|
||||
async function openEdit(node: CategoryNode) {
|
||||
actionOpen.value = false
|
||||
resetForm()
|
||||
await refreshParentOptions()
|
||||
await getCategoryTreeSelect()
|
||||
formData.id = node.id
|
||||
formData.parentId = node.parentId || ''
|
||||
formData.categoryName = node.categoryName || ''
|
||||
@@ -391,12 +374,24 @@
|
||||
actionOpen.value = true
|
||||
}
|
||||
|
||||
function onActionSelect(action: { value?: string }) {
|
||||
function onActionSelect(action: { value?: string; disabled?: boolean }) {
|
||||
const node = actionNode.value
|
||||
if (!node) return
|
||||
if (action.value === 'add') void openAdd(node)
|
||||
else if (action.value === 'edit') void openEdit(node)
|
||||
else if (action.value === 'delete') void onActionDelete()
|
||||
if (action.value === 'add') {
|
||||
void openAdd(node)
|
||||
return
|
||||
}
|
||||
if (action.value === 'edit') {
|
||||
void openEdit(node)
|
||||
return
|
||||
}
|
||||
if (action.value === 'delete') {
|
||||
if (!canDeleteActionNode.value || action.disabled) {
|
||||
showFailToast(CREATOR_DELETE_TIP)
|
||||
return
|
||||
}
|
||||
void onActionDelete()
|
||||
}
|
||||
}
|
||||
|
||||
async function onActionDelete() {
|
||||
@@ -428,15 +423,6 @@
|
||||
function closeForm() {
|
||||
if (!submitLoading.value) formOpen.value = false
|
||||
}
|
||||
function onParentConfirm({ selectedValues }: { selectedValues: string[] }) {
|
||||
const v = selectedValues[0] || '0'
|
||||
formData.parentId = v === '0' ? '' : v
|
||||
parentPickerOpen.value = false
|
||||
}
|
||||
function onStatusConfirm({ selectedValues }: { selectedValues: string[] }) {
|
||||
formData.status = selectedValues[0] || '0'
|
||||
statusPickerOpen.value = false
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
if (!formData.categoryName.trim()) {
|
||||
|
||||
Reference in new issue
Block a user