feat(resource): 对接急救宣教资源管理 V3 接口
- 审批与上架分离:已通过状态显示「已通过」+「上架」操作 - 分类扁平化:分类表单去掉父级分类字段 - 标签从分类 tags 字段联动取,不再调独立标签接口 - 标签表单加所属分类必填,支持从分类「+标签」按钮预设锁定 - 审核记录字段对齐后端:resourceTitle/auditRejectReason - 资源表单 footer 隐藏默认按钮,写操作 API 关全局提示避免重复弹
This commit is contained in:
@@ -50,6 +50,7 @@
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a-button type="link" size="small" @click="handlePreview(record)">查看</a-button>
|
||||
<a-button type="link" size="small" @click="handlePublish(record)">上架</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
@@ -123,6 +124,7 @@
|
||||
approveApi,
|
||||
rejectApi,
|
||||
} from './check.api';
|
||||
import { publishApi } from '/@/views/resource/resource.api';
|
||||
import dayjs from 'dayjs';
|
||||
import { CONTENT_TYPE } from '/@/views/resource/resource.data';
|
||||
import PreviewModal from '/@/views/resource/components/PreviewModal.vue';
|
||||
@@ -174,11 +176,11 @@
|
||||
];
|
||||
|
||||
const recordColumns = [
|
||||
{ title: '资源名称', dataIndex: 'title' },
|
||||
{ title: '资源名称', dataIndex: 'resourceTitle' },
|
||||
{ title: '操作', dataIndex: 'action', align: 'center', width: 100 },
|
||||
{ title: '审核人', dataIndex: 'auditBy', align: 'center', width: 120 },
|
||||
{ title: '时间', dataIndex: 'auditTime', align: 'center', width: 170 },
|
||||
{ title: '意见', dataIndex: 'auditOpinion' },
|
||||
{ title: '意见', dataIndex: 'auditRejectReason' },
|
||||
];
|
||||
|
||||
function getTypeLabel(type: string) {
|
||||
@@ -210,6 +212,13 @@
|
||||
createMessage.info(`重新编辑:${record.title}(跳转到资源管理编辑)`);
|
||||
}
|
||||
|
||||
// V3:已通过的资源需手动上架
|
||||
async function handlePublish(record: any) {
|
||||
await publishApi({ id: record.id });
|
||||
createMessage.success('已上架');
|
||||
loadPassed();
|
||||
}
|
||||
|
||||
async function loadPending() {
|
||||
pendingLoading.value = true;
|
||||
try {
|
||||
|
||||
@@ -4,17 +4,6 @@
|
||||
<a-form-item label="分类名称" required>
|
||||
<a-input v-model:value="formState.name" placeholder="请输入分类名称" :maxlength="50" />
|
||||
</a-form-item>
|
||||
<a-form-item label="父级分类">
|
||||
<a-tree-select
|
||||
v-model:value="formState.parentId"
|
||||
:tree-data="categoryTreeData"
|
||||
:field-names="{ label: 'name', value: 'id', children: 'children' }"
|
||||
placeholder="顶级分类(不选)"
|
||||
allow-clear
|
||||
tree-default-expand-all
|
||||
:disabled="!!formState.id"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号">
|
||||
<a-input-number v-model:value="formState.sortNo" :min="0" style="width: 100%" />
|
||||
</a-form-item>
|
||||
@@ -26,20 +15,19 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ref, reactive } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { categoryListAllApi, categoryAddApi, categoryEditApi, buildTree } from '../label.api';
|
||||
import { categoryAddApi, categoryEditApi } from '../label.api';
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const { createMessage } = useMessage();
|
||||
const title = ref('新增分类');
|
||||
const categoryTreeData = ref<any[]>([]);
|
||||
|
||||
// V3:分类扁平化,仅一级,不再有 parentId
|
||||
const formState = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
parentId: undefined as string | undefined,
|
||||
sortNo: 1,
|
||||
description: '',
|
||||
});
|
||||
@@ -49,21 +37,10 @@
|
||||
title.value = isUpdate ? '编辑分类' : '新增分类';
|
||||
formState.id = data?.record?.id || '';
|
||||
formState.name = data?.record?.name || '';
|
||||
formState.parentId = data?.record?.parentId || (data?.parentId || undefined);
|
||||
formState.sortNo = data?.record?.sortNo ?? 1;
|
||||
formState.description = data?.record?.description || '';
|
||||
// 编辑时父级分类禁用(避免循环引用)
|
||||
if (isUpdate) {
|
||||
formState.parentId = data?.record?.parentId || undefined;
|
||||
}
|
||||
await loadCategoryTree();
|
||||
});
|
||||
|
||||
async function loadCategoryTree() {
|
||||
const list = (await categoryListAllApi()) || [];
|
||||
categoryTreeData.value = buildTree(list);
|
||||
}
|
||||
|
||||
function validate(): boolean {
|
||||
if (!formState.name) {
|
||||
createMessage.warning('请输入分类名称');
|
||||
@@ -79,7 +56,6 @@
|
||||
const params = {
|
||||
id: formState.id || undefined,
|
||||
name: formState.name,
|
||||
parentId: formState.parentId || undefined,
|
||||
sortNo: formState.sortNo,
|
||||
description: formState.description,
|
||||
};
|
||||
@@ -96,8 +72,4 @@
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadCategoryTree();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" :width="480" :minHeight="260" @ok="handleSubmit">
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="所属分类" required>
|
||||
<a-select
|
||||
v-model:value="formState.parentId"
|
||||
:options="categoryOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
placeholder="请选择所属分类"
|
||||
:disabled="!!lockParentId"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="标签名称" required>
|
||||
<a-input v-model:value="formState.name" placeholder="请输入标签名称" :maxlength="20" />
|
||||
</a-form-item>
|
||||
@@ -15,18 +25,23 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { tagAddApi, tagEditApi } from '../label.api';
|
||||
import { categoryListAllApi } from '/@/views/resource/resource.api';
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const { createMessage } = useMessage();
|
||||
const title = ref('新增标签');
|
||||
const categoryOptions = ref<any[]>([]);
|
||||
// 从分类的「+标签」按钮进来时锁定所属分类
|
||||
const lockParentId = ref('');
|
||||
|
||||
const formState = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
parentId: '' as string,
|
||||
sortNo: 1,
|
||||
description: '',
|
||||
});
|
||||
@@ -36,11 +51,19 @@
|
||||
title.value = isUpdate ? '编辑标签' : '新增标签';
|
||||
formState.id = data?.record?.id || '';
|
||||
formState.name = data?.record?.name || '';
|
||||
// V3:支持从分类「+标签」按钮进来时预设 parentId 并锁定
|
||||
const presetParentId = data?.parentId || data?.record?.parentId || '';
|
||||
formState.parentId = presetParentId;
|
||||
lockParentId.value = data?.parentId ? presetParentId : '';
|
||||
formState.sortNo = data?.record?.sortNo ?? 1;
|
||||
formState.description = data?.record?.description || '';
|
||||
});
|
||||
|
||||
function validate(): boolean {
|
||||
if (!formState.parentId) {
|
||||
createMessage.warning('请选择所属分类');
|
||||
return false;
|
||||
}
|
||||
if (!formState.name) {
|
||||
createMessage.warning('请输入标签名称');
|
||||
return false;
|
||||
@@ -55,6 +78,7 @@
|
||||
const params = {
|
||||
id: formState.id || undefined,
|
||||
name: formState.name,
|
||||
parentId: formState.parentId,
|
||||
sortNo: formState.sortNo,
|
||||
description: formState.description,
|
||||
};
|
||||
@@ -71,4 +95,12 @@
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
categoryOptions.value = (await categoryListAllApi()) || [];
|
||||
} catch {
|
||||
categoryOptions.value = [];
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
+25
-17
@@ -29,7 +29,7 @@
|
||||
<a-tag v-if="status === 2" color="red" style="margin-left: 4px">冻结</a-tag>
|
||||
<span class="tree-actions">
|
||||
<a-button type="link" size="small" @click.stop="handleEditCategory({ id, name })">编</a-button>
|
||||
<a-button type="link" size="small" @click.stop="handleAddSubCategory(id)">+</a-button>
|
||||
<a-button type="link" size="small" @click.stop="handleAddTagUnderCategory(id)">+标签</a-button>
|
||||
<a-button type="link" size="small" danger @click.stop="handleDeleteCategory({ id, name })">删</a-button>
|
||||
</span>
|
||||
</div>
|
||||
@@ -93,7 +93,7 @@
|
||||
<!-- 分类新增/编辑弹窗 -->
|
||||
<CategoryFormModal @register="registerCategoryModal" @success="loadCategoryTree" />
|
||||
<!-- 标签新增/编辑弹窗 -->
|
||||
<TagFormModal @register="registerTagModal" @success="loadTags" />
|
||||
<TagFormModal @register="registerTagModal" @success="loadCategoryTree" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -105,7 +105,6 @@
|
||||
import {
|
||||
categoryListAllApi,
|
||||
categoryDeleteApi,
|
||||
tagListAllApi,
|
||||
tagDeleteApi,
|
||||
buildTree,
|
||||
} from './label.api';
|
||||
@@ -144,9 +143,13 @@
|
||||
|
||||
const categoryTreeSelectData = computed(() => categoryTree.value);
|
||||
|
||||
function onSelectCategory(keys: string[], info: any) {
|
||||
function onSelectCategory(keys: string[]) {
|
||||
// V3:标签数据从分类的 tags 字段取,不再单独调接口
|
||||
if (keys.length > 0) {
|
||||
createMessage.info(`已选中分类:${info.node.name}`);
|
||||
const category = categoryList.value.find((c) => c.id === keys[0]);
|
||||
tagList.value = category?.tags || [];
|
||||
} else {
|
||||
tagList.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,9 +158,15 @@
|
||||
}
|
||||
|
||||
function handleAddSubCategory(parentId: string) {
|
||||
// V3:分类扁平化,此函数已废弃,保留以防其他地方调用
|
||||
openCategoryModal(true, { isUpdate: false, parentId });
|
||||
}
|
||||
|
||||
// V3:分类项后面的「+标签」按钮,新增该分类下的标签
|
||||
function handleAddTagUnderCategory(parentId: string) {
|
||||
openTagModal(true, { isUpdate: false, parentId });
|
||||
}
|
||||
|
||||
function handleEditCategory(record: any) {
|
||||
openCategoryModal(true, { isUpdate: true, record });
|
||||
}
|
||||
@@ -195,7 +204,7 @@
|
||||
function handleDeleteTag(tag: any) {
|
||||
tagDeleteApi({ id: tag.id }, () => {
|
||||
createMessage.success('已删除标签');
|
||||
loadTags();
|
||||
loadCategoryTree();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -255,24 +264,23 @@
|
||||
if (expandedKeys.value.length === 0 && categoryTree.value.length > 0) {
|
||||
expandedKeys.value = [categoryTree.value[0].id];
|
||||
}
|
||||
// V3:默认选中第一个分类,标签从分类的 tags 字段取
|
||||
if (selectedKeys.value.length === 0 && categoryList.value.length > 0) {
|
||||
const first = categoryList.value[0];
|
||||
selectedKeys.value = [first.id];
|
||||
tagList.value = first?.tags || [];
|
||||
} else if (selectedKeys.value.length > 0) {
|
||||
// 已选中分类,重新拉数据后刷新该分类的标签(新增/编辑/删除标签后回显最新数据)
|
||||
const selected = categoryList.value.find((c) => c.id === selectedKeys.value[0]);
|
||||
tagList.value = selected?.tags || [];
|
||||
}
|
||||
} finally {
|
||||
categoryLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTags() {
|
||||
tagLoading.value = true;
|
||||
try {
|
||||
const res = await tagListAllApi();
|
||||
tagList.value = res || [];
|
||||
} finally {
|
||||
tagLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadCategoryTree();
|
||||
loadTags();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ export const categoryListAllApi = (params?: any) => defHttp.get({ url: Api.categ
|
||||
export const categoryListRootApi = (params?: any) => defHttp.get({ url: Api.categoryListRoot, params });
|
||||
export const categoryListChildrenApi = (params: any) => defHttp.get({ url: Api.categoryListChildren, params });
|
||||
export const categoryQueryByIdApi = (params: any) => defHttp.get({ url: Api.categoryQueryById, params });
|
||||
export const categoryAddApi = (params: any) => defHttp.post({ url: Api.categoryAdd, params });
|
||||
export const categoryEditApi = (params: any) => defHttp.post({ url: Api.categoryEdit, params });
|
||||
export const categoryAddApi = (params: any) => defHttp.post({ url: Api.categoryAdd, params }, { successNeedMessage: false });
|
||||
export const categoryEditApi = (params: any) => defHttp.post({ url: Api.categoryEdit, params }, { successNeedMessage: false });
|
||||
|
||||
export const categoryDeleteApi = (params: any, handleSuccess: any) => {
|
||||
createConfirm({
|
||||
@@ -39,7 +39,7 @@ export const categoryDeleteApi = (params: any, handleSuccess: any) => {
|
||||
title: '确认删除',
|
||||
content: '是否删除该分类?',
|
||||
onOk: async () => {
|
||||
await defHttp.post({ url: Api.categoryDelete, params });
|
||||
await defHttp.post({ url: Api.categoryDelete, params }, { successNeedMessage: false });
|
||||
handleSuccess();
|
||||
},
|
||||
});
|
||||
@@ -51,7 +51,7 @@ export const categoryDeleteBatchApi = (params: any, handleSuccess: any) => {
|
||||
title: '批量删除',
|
||||
content: `是否删除选中的 ${params?.length || 0} 个分类?`,
|
||||
onOk: async () => {
|
||||
await defHttp.post({ url: Api.categoryDeleteBatch, params });
|
||||
await defHttp.post({ url: Api.categoryDeleteBatch, params }, { successNeedMessage: false });
|
||||
handleSuccess();
|
||||
},
|
||||
});
|
||||
@@ -61,8 +61,8 @@ export const categoryDeleteBatchApi = (params: any, handleSuccess: any) => {
|
||||
export const tagListApi = (params: any) => defHttp.get({ url: Api.tagList, params });
|
||||
export const tagListAllApi = (params?: any) => defHttp.get({ url: Api.tagListAll, params });
|
||||
export const tagQueryByIdApi = (params: any) => defHttp.get({ url: Api.tagQueryById, params });
|
||||
export const tagAddApi = (params: any) => defHttp.post({ url: Api.tagAdd, params });
|
||||
export const tagEditApi = (params: any) => defHttp.post({ url: Api.tagEdit, params });
|
||||
export const tagAddApi = (params: any) => defHttp.post({ url: Api.tagAdd, params }, { successNeedMessage: false });
|
||||
export const tagEditApi = (params: any) => defHttp.post({ url: Api.tagEdit, params }, { successNeedMessage: false });
|
||||
|
||||
export const tagDeleteApi = (params: any, handleSuccess: any) => {
|
||||
createConfirm({
|
||||
@@ -70,7 +70,7 @@ export const tagDeleteApi = (params: any, handleSuccess: any) => {
|
||||
title: '确认删除',
|
||||
content: '是否删除该标签?',
|
||||
onOk: async () => {
|
||||
await defHttp.post({ url: Api.tagDelete, params });
|
||||
await defHttp.post({ url: Api.tagDelete, params }, { successNeedMessage: false });
|
||||
handleSuccess();
|
||||
},
|
||||
});
|
||||
@@ -82,7 +82,7 @@ export const tagDeleteBatchApi = (params: any, handleSuccess: any) => {
|
||||
title: '批量删除',
|
||||
content: `是否删除选中的 ${params?.length || 0} 个标签?`,
|
||||
onOk: async () => {
|
||||
await defHttp.post({ url: Api.tagDeleteBatch, params });
|
||||
await defHttp.post({ url: Api.tagDeleteBatch, params }, { successNeedMessage: false });
|
||||
handleSuccess();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" :width="900" :minHeight="500" :useDefaultFooter="false" @ok="handleSubmit">
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" :width="900" :minHeight="500" :useDefaultFooter="false" :showCancelBtn="false" :showOkBtn="false">
|
||||
<div class="resource-form">
|
||||
<a-form layout="vertical" :model="formState">
|
||||
<a-row :gutter="16">
|
||||
@@ -33,9 +33,10 @@
|
||||
<a-select
|
||||
v-model:value="formState.tagIdList"
|
||||
mode="multiple"
|
||||
:options="tagOptions"
|
||||
placeholder="请选择标签"
|
||||
:options="currentCategoryTags"
|
||||
placeholder="请先选择分类"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
:disabled="!formState.categoryId"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -88,11 +89,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ref, reactive, computed, onMounted } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { TYPE_OPTIONS, SCENE_OPTIONS } from '../resource.data';
|
||||
import { addApi, editApi, submitForReviewApi, categoryListAllApi, tagListAllApi } from '../resource.api';
|
||||
import { addApi, editApi, submitForReviewApi, categoryListAllApi } from '../resource.api';
|
||||
import JImageUpload from '/@/components/Form/src/jeecg/components/JImageUpload.vue';
|
||||
import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
|
||||
import JEditor from '/@/components/Form/src/jeecg/components/JEditor.vue';
|
||||
@@ -101,10 +102,16 @@
|
||||
const { createMessage } = useMessage();
|
||||
const title = ref('新增急救宣教资源');
|
||||
const categoryOptions = ref<any[]>([]);
|
||||
const tagOptions = ref<any[]>([]);
|
||||
// 附件列表(JUpload 用 array 绑定,提交时 JSON.stringify 为后端 fileUrl 字段)
|
||||
const fileList = ref<any[]>([]);
|
||||
|
||||
// V3:标签从分类的 tags 字段取,选择分类后联动
|
||||
const currentCategoryTags = computed(() => {
|
||||
if (!formState.categoryId) return [];
|
||||
const category = categoryOptions.value.find((c) => c.id === formState.categoryId);
|
||||
return category?.tags || [];
|
||||
});
|
||||
|
||||
const formState = reactive({
|
||||
id: '',
|
||||
title: '',
|
||||
@@ -233,8 +240,8 @@
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// V3:标签从分类的 tags 字段取,不再单独调接口
|
||||
categoryOptions.value = (await categoryListAllApi()) || [];
|
||||
tagOptions.value = (await tagListAllApi()) || [];
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -200,10 +200,10 @@
|
||||
},
|
||||
});
|
||||
} else if (record.status === STATUS.DRAFT && record.auditStatus === AUDIT_STATUS.PASSED) {
|
||||
// 已通过的草稿状态(编辑后自动退回草稿),可重新提交
|
||||
// V3:已通过待上架状态,操作为「上架」
|
||||
actions.push({
|
||||
label: '提交审核',
|
||||
onClick: () => handleSubmitReview(record),
|
||||
label: '上架',
|
||||
onClick: () => handlePublish(record),
|
||||
});
|
||||
}
|
||||
return actions;
|
||||
|
||||
@@ -33,10 +33,12 @@ export const CONTENT_TYPE = {
|
||||
};
|
||||
|
||||
// 根据发布状态+审核状态返回展示信息
|
||||
// V3:审核通过但未上架 (auditStatus=11 && status=0) 显示为「已通过」
|
||||
export function getResourceDisplayStatus(status: number, auditStatus: number | null) {
|
||||
if (status === STATUS.PUBLISHED) return { text: '已发布', color: 'success' };
|
||||
if (status === STATUS.OFFLINE) return { text: '已下架', color: 'error' };
|
||||
if (auditStatus === AUDIT_STATUS.PENDING) return { text: '待审核', color: 'warning' };
|
||||
if (auditStatus === AUDIT_STATUS.PASSED) return { text: '已通过', color: 'processing' };
|
||||
if (auditStatus === AUDIT_STATUS.REJECTED) return { text: '已驳回', color: 'error' };
|
||||
return { text: '草稿', color: 'default' };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user