feat(emergency): 实现急救宣教推送到 IM 会话功能

- 新增 FirstAidPush 组件:分类筛选 + 搜索 + 单条/一键推送
- IM 端切换会话时 postMessage 携带 orderId 通知父页面
- 父页面监听 sessionChanged 联动推送按钮与后端调用
- 推送调后端 push/send 记录状态 + IM postMessage 推卡片
- 资源管理正文改富文本编辑器,修批量操作重复提示
- 重建 iweb 产物
This commit is contained in:
wanghao
2026-06-24 17:23:36 +08:00
parent a2a0e274f4
commit 6bb4b97ff4
21 changed files with 993 additions and 234 deletions
+28 -122
View File
@@ -22,9 +22,10 @@
block-node
@select="onSelectCategory"
>
<template #title="{ name, id, status }">
<template #title="{ name, id, status, resourceCount }">
<div class="tree-node-content">
<span class="tree-name">{{ name }}</span>
<span v-if="resourceCount != null" class="tree-count">({{ resourceCount }})</span>
<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>
@@ -61,6 +62,7 @@
@close.prevent="handleDeleteTag(tag)"
>
{{ tag.name }}
<span v-if="tag.resourceCount != null" class="tag-count">{{ tag.resourceCount }}</span>
</a-tag>
<a-empty v-if="filteredTags.length === 0 && !tagLoading" description="暂无标签" />
</div>
@@ -68,7 +70,7 @@
</a-card>
<a-card title="分类权限配置" :bordered="false" class="card-section">
<a-alert message="权限矩阵接口后端尚未提供,当前为占位展示" type="info" show-icon style="margin-bottom: 12px" />
<!-- <a-alert message="权限矩阵接口后端尚未提供,当前为占位展示" type="info" show-icon style="margin-bottom: 12px" />-->
<a-table
:columns="permissionColumns"
:data-source="permissionList"
@@ -89,50 +91,9 @@
</a-row>
<!-- 分类新增/编辑弹窗 -->
<a-modal
v-model:open="categoryModalVisible"
:title="categoryModalTitle"
@ok="handleCategorySubmit"
:width="480"
:confirm-loading="categorySubmitting"
>
<a-form layout="vertical">
<a-form-item label="分类名称" required>
<a-input v-model:value="categoryForm.name" placeholder="请输入分类名称" />
</a-form-item>
<a-form-item label="父级分类">
<a-tree-select
v-model:value="categoryForm.parentId"
:tree-data="categoryTreeSelectData"
:field-names="{ label: 'name', value: 'id', children: 'children' }"
placeholder="顶级分类(不选)"
allow-clear
tree-default-expand-all
/>
</a-form-item>
<a-form-item label="排序号">
<a-input-number v-model:value="categoryForm.sortNo" :min="0" style="width: 100%" />
</a-form-item>
<a-form-item label="描述">
<a-textarea v-model:value="categoryForm.description" :rows="2" placeholder="可选" />
</a-form-item>
</a-form>
</a-modal>
<!-- 标签新增弹窗 -->
<a-modal v-model:open="tagModalVisible" title="新增标签" @ok="handleTagSubmit" :width="480" :confirm-loading="tagSubmitting">
<a-form layout="vertical">
<a-form-item label="标签名称" required>
<a-input v-model:value="tagForm.name" placeholder="请输入标签名称" />
</a-form-item>
<a-form-item label="排序号">
<a-input-number v-model:value="tagForm.sortNo" :min="0" style="width: 100%" />
</a-form-item>
<a-form-item label="描述">
<a-textarea v-model:value="tagForm.description" :rows="2" placeholder="可选" />
</a-form-item>
</a-form>
</a-modal>
<CategoryFormModal @register="registerCategoryModal" @success="loadCategoryTree" />
<!-- 标签新增/编辑弹窗 -->
<TagFormModal @register="registerTagModal" @success="loadTags" />
</div>
</template>
@@ -140,20 +101,22 @@
import { ref, reactive, computed, onMounted } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { useModal } from '/@/components/Modal';
import {
categoryListAllApi,
categoryQueryByIdApi,
categoryAddApi,
categoryEditApi,
categoryDeleteApi,
tagListAllApi,
tagAddApi,
tagDeleteApi,
buildTree,
} from './label.api';
import CategoryFormModal from './components/CategoryFormModal.vue';
import TagFormModal from './components/TagFormModal.vue';
const { createMessage } = useMessage();
const [registerCategoryModal, { openModal: openCategoryModal }] = useModal();
const [registerTagModal, { openModal: openTagModal }] = useModal();
// ============ 分类树 ============
const categoryList = ref<any[]>([]);
const categoryLoading = ref(false);
@@ -188,29 +151,15 @@
}
function handleAddCategory(parentId?: string) {
categoryModalTitle.value = '新增分类';
categoryForm.id = '';
categoryForm.name = '';
categoryForm.parentId = parentId || undefined;
categoryForm.sortNo = 1;
categoryForm.description = '';
categoryModalVisible.value = true;
openCategoryModal(true, { isUpdate: false, parentId });
}
function handleAddSubCategory(parentId: string) {
handleAddCategory(parentId);
openCategoryModal(true, { isUpdate: false, parentId });
}
async function handleEditCategory(record: any) {
// 拉详情获取完整字段
const detail = await categoryQueryByIdApi({ id: record.id }).catch(() => null);
categoryModalTitle.value = '编辑分类';
categoryForm.id = detail?.id || record.id;
categoryForm.name = detail?.name || record.name;
categoryForm.parentId = detail?.parentId || undefined;
categoryForm.sortNo = detail?.sortNo ?? 1;
categoryForm.description = detail?.description || '';
categoryModalVisible.value = true;
function handleEditCategory(record: any) {
openCategoryModal(true, { isUpdate: true, record });
}
function handleDeleteCategory(record: any) {
@@ -220,35 +169,6 @@
});
}
async function handleCategorySubmit() {
if (!categoryForm.name) {
createMessage.warning('请输入分类名称');
return;
}
categorySubmitting.value = true;
try {
const payload = {
id: categoryForm.id || undefined,
name: categoryForm.name,
parentId: categoryForm.parentId,
sortNo: categoryForm.sortNo,
description: categoryForm.description,
status: 1,
};
if (categoryForm.id) {
await categoryEditApi(payload);
createMessage.success('已更新分类');
} else {
await categoryAddApi(payload);
createMessage.success('已新增分类');
}
categoryModalVisible.value = false;
loadCategoryTree();
} finally {
categorySubmitting.value = false;
}
}
// ============ 标签 ============
const tagList = ref<any[]>([]);
const tagLoading = ref(false);
@@ -269,10 +189,7 @@
}
function handleAddTag() {
tagForm.name = '';
tagForm.sortNo = 1;
tagForm.description = '';
tagModalVisible.value = true;
openTagModal(true, { isUpdate: false });
}
function handleDeleteTag(tag: any) {
@@ -282,27 +199,6 @@
});
}
async function handleTagSubmit() {
if (!tagForm.name) {
createMessage.warning('请输入标签名称');
return;
}
tagSubmitting.value = true;
try {
await tagAddApi({
name: tagForm.name,
sortNo: tagForm.sortNo,
description: tagForm.description,
status: 1,
});
createMessage.success('已新增标签');
tagModalVisible.value = false;
loadTags();
} finally {
tagSubmitting.value = false;
}
}
// ============ 权限矩阵(mock,后端未提供接口) ============
const permissionList = ref<any[]>([
{ role: '系统管理员', permissions: ['rw', 'rw', 'rw', 'rw', 'rw'] },
@@ -400,6 +296,11 @@
.tree-name {
flex: 1;
}
.tree-count {
font-size: 11px;
color: #999;
margin-left: 4px;
}
.tree-actions {
display: none;
margin-left: auto;
@@ -419,4 +320,9 @@
padding: 4px 14px;
cursor: default;
}
.tag-count {
opacity: 0.5;
font-size: 11px;
margin-left: 4px;
}
</style>