Compare commits

..

No commits in common. "ed2c45bc80f8eaa2ccc0d7e3ea9936f228dcfbb6" and "11ed08bc39e049406142fed326bae96e9e11323e" have entirely different histories.

3 changed files with 308 additions and 197 deletions

View File

@ -1,11 +0,0 @@
// 服务设施模块
import request from '@/utils/request'
// 获取节点
export const getNodeList = (params) => request.basic.get('/api/v1/service-nodes', params)
// 创建节点
export const createNode = (params) => request.basic.post('/api/v1/service-nodes', params)
// 删除节点
export const delNode = (id) => request.basic.delete(`/api/v1/service-nodes/${id}`)

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="org-management"> <div class="org-management">
<div class="header" v-if="!hasData"> <!-- <div class="header">
<h1>组织管理</h1> <h1>组织管理</h1>
<a-button type="primary" @click="showAddModal()">添加根节点</a-button> <a-button type="primary" @click="showAddModal()">添加根节点</a-button>
</div> </div> -->
<div class="org-table"> <div class="org-table">
<a-table <a-table
@ -14,30 +14,27 @@
:expand-icon-as-cell="false" :expand-icon-as-cell="false"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'disabled'"> <template v-if="column.key === 'projectType'">
<a-tag :color="record.disabled === false ? 'green' : 'red'"> <a-tag :color="record.projectType === 'Supervision' ? 'blue' : 'green'">
{{ record.disabled === false ? '启用' : '禁用' }} {{ record.projectType === 'Supervision' ? '监管' : '居家养老床位' }}
</a-tag> </a-tag>
</template> </template>
<template v-if="column.key === 'created_at'"> <template v-if="column.key === 'status'">
<div>{{ record.created_at ? dayjs(record.created_at).format('YYYY-MM-DD HH:mm') : '-' }}</div> <a-tag :color="record.status === 0 ? 'green' : 'red'">
</template> {{ record.status === 0 ? '启用' : '禁用' }}
<template v-if="column.key === 'updated_at'"> </a-tag>
<div>{{ record.updated_at ? dayjs(record.updated_at).format('YYYY-MM-DD HH:mm') : '-' }}</div>
</template> </template>
<template v-if="column.key === 'actions'"> <template v-if="column.key === 'actions'">
<div class="actions"> <div class="actions">
<!-- 修改这里只在前两层显示添加子节点按钮 -->
<a-button <a-button
v-if="getNodeLevel(record) < 2"
size="small" size="small"
type="primary" type="primary"
@click="showAddModal(record)" @click="showAddModal(record)"
v-if="record.projectType === 'Supervision'"
> >
添加子节点 添加子节点
</a-button> </a-button>
<a-button <a-button
style="margin-left: 20px;"
size="small" size="small"
danger danger
@click="handleDelete(record)" @click="handleDelete(record)"
@ -67,13 +64,17 @@
<a-form-item label="节点名称" name="name"> <a-form-item label="节点名称" name="name">
<a-input v-model:value="formState.name" placeholder="请输入节点名称" /> <a-input v-model:value="formState.name" placeholder="请输入节点名称" />
</a-form-item> </a-form-item>
<a-form-item label="节点类型" name="projectType">
<a-select v-model:value="formState.projectType" placeholder="请选择节点类型">
<a-select-option value="Supervision">监管</a-select-option>
<a-select-option value="HomeCareBed">居家养老床位</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="状态" name="status"> <a-form-item label="状态" name="status">
<a-switch <a-radio-group v-model:value="formState.status">
v-model:checked="formState.status" <a-radio :value="0">启用</a-radio>
:checked-children="statusText.checked" <a-radio :value="1">禁用</a-radio>
:un-checked-children="statusText.unchecked" </a-radio-group>
:disabled="!isEditing"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
@ -81,34 +82,15 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted, computed } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import apis from '@/apis'; import { message, Modal } from 'ant-design-vue';
import { Modal, message } from 'ant-design-vue';
import dayjs from 'dayjs';
import { config } from '@/config';
import { usePagination } from '@/hooks';
const { listData, paginationState, loading, showLoading, hideLoading, resetPagination, searchFormData } = usePagination();
//
const hasData = computed(() => {
return listData.value && listData.value.length > 0;
});
paginationState.onChange = (page, pageSize) => {
paginationState.current = page
paginationState.pageSize = pageSize
getList()
}
// subOrgList children // subOrgList children
const convertData = (data) => { const convertData = (data) => {
if (!data || data.length === 0) return [];
return data.map(item => { return data.map(item => {
const converted = { const converted = {
...item, ...item,
key: item.id, // 使 id key id orgId key: item.orgId, // 使 orgId key
}; };
if (item.subOrgList && item.subOrgList.length > 0) { if (item.subOrgList && item.subOrgList.length > 0) {
@ -119,6 +101,122 @@ const convertData = (data) => {
}); });
}; };
//
const originalData = ref([
{
"id": 5066,
"orgId": "1813150290069417984",
"name": "南通市通州区互联网+智慧养老居家上门服务项目",
"path": "/0/1813150290069417984",
"parentId": "0",
"status": 0,
"tenantId": "1813150290048446464",
"projectType": "Supervision",
"subOrgList": [
{
"id": 5093,
"orgId": "1813400548804390913",
"name": "东社镇",
"parentId": "1813150290069417984",
"path": "/0/1813150290069417984/1813400548804390913",
"status": 0,
"tenantId": "1813150290048446464",
"projectType": "Supervision",
"createBy": "1694238554834726912",
"createTime": "2024-07-17T10:29:17",
"updateBy": "1694238554834726912",
"updateTime": "2024-07-17T10:29:17",
"subOrgList": [
{
"id": 5094,
"orgId": "1813400640336556032",
"name": "东社镇服务站",
"parentId": "1813400548804390913",
"path": "/0/1813150290069417984/1813400548804390913/1813400640336556032",
"status": 0,
"tenantId": "1813150290048446464",
"projectType": "HomeCareBed",
"createBy": "1694238554834726912",
"createTime": "2024-07-17T10:29:39",
"updateBy": "1694238554834726912",
"updateTime": "2024-07-17T10:29:39",
"subOrgList": [],
"disabled": false
}
],
"disabled": false
},
{
"id": 5071,
"orgId": "1813396942931881985",
"name": "二甲镇",
"parentId": "1813150290069417984",
"path": "/0/1813150290069417984/1813396942931881985",
"status": 0,
"tenantId": "1813150290048446464",
"projectType": "Supervision",
"createBy": "1694238554834726912",
"createTime": "2024-07-17T10:14:57",
"updateBy": "1694238554834726912",
"updateTime": "2024-07-17T10:14:57",
"subOrgList": [
{
"id": 5072,
"orgId": "1813397035642646528",
"name": "二甲镇服务站",
"parentId": "1813396942931881985",
"path": "/0/1813150290069417984/1813396942931881985/1813397035642646528",
"status": 0,
"tenantId": "1813150290048446464",
"projectType": "HomeCareBed",
"createBy": "1694238554834726912",
"createTime": "2024-07-17T10:15:19",
"updateBy": "1694238554834726912",
"updateTime": "2024-07-17T10:15:19",
"subOrgList": [],
"disabled": false
}
],
"disabled": false
},
{
"id": 5091,
"orgId": "1813400338249674753",
"name": "五接镇",
"parentId": "1813150290069417984",
"path": "/0/1813150290069417984/1813400338249674753",
"status": 0,
"tenantId": "1813150290048446464",
"projectType": "Supervision",
"createBy": "1694238554834726912",
"createTime": "2024-07-17T10:28:27",
"updateBy": "1694238554834726912",
"updateTime": "2024-07-17T10:28:27",
"subOrgList": [
{
"id": 5092,
"orgId": "1813400446438469632",
"name": "五接镇服务站",
"parentId": "1813400338249674753",
"path": "/0/1813150290069417984/1813400338249674753/1813400446438469632",
"status": 0,
"tenantId": "1813150290048446464",
"projectType": "HomeCareBed",
"createBy": "1694238554834726912",
"createTime": "2024-07-17T10:28:53",
"updateBy": "1694238554834726912",
"updateTime": "2024-07-17T10:28:53",
"subOrgList": [],
"disabled": false
}
],
"disabled": false
}
],
"disabled": false
}
]);
// //
const dataSource = ref([]); const dataSource = ref([]);
@ -128,51 +226,52 @@ const isEditing = ref(false);
const currentParent = ref(null); const currentParent = ref(null);
const confirmLoading = ref(false); const confirmLoading = ref(false);
// - false //
const formState = reactive({ const formState = reactive({
name: '', name: '',
status: false // false projectType: 'Supervision',
}); status: 0
//
const statusText = computed(() => {
return {
checked: '禁用',
unchecked: '启用'
};
}); });
// //
const rules = { const rules = {
name: [ name: [
{ required: true, message: '请输入节点名称', trigger: 'blur' } { required: true, message: '请输入节点名称', trigger: 'blur' }
],
projectType: [
{ required: true, message: '请选择节点类型', trigger: 'change' }
] ]
}; };
// - projectType //
const columns = [ const columns = [
{ {
title: '组织名称', title: '组织名称',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
width: 200, },
{
title: '组织类型',
dataIndex: 'projectType',
key: 'projectType',
width: 150,
}, },
{ {
title: '状态', title: '状态',
dataIndex: 'disabled', dataIndex: 'status',
key: 'disabled', key: 'status',
width: 100, width: 100,
}, },
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'created_at', dataIndex: 'createTime',
key: 'created_at', key: 'createTime',
width: 180, width: 180,
}, },
{ {
title: '更新时间', title: '更新时间',
dataIndex: 'updated_at', dataIndex: 'updateTime',
key: 'updated_at', key: 'updateTime',
width: 180, width: 180,
}, },
{ {
@ -182,110 +281,49 @@ const columns = [
}, },
]; ];
/** //
* 获取节点的层级 const initData = () => {
* @param {Object} node 节点数据 dataSource.value = convertData(originalData.value);
* @param {Array} data 数据源
* @param {number} level 当前层级
* @returns {number} 节点层级0表示第一层1表示第二层2表示第三层
*/
const getNodeLevel = (node, data = null, level = 0) => {
// 0
if (!node.parentId || node.parentId === "") {
return 0;
}
// data使dataSource
const searchData = data || dataSource.value;
//
const findParentLevel = (nodes, targetId, currentLevel) => {
for (const item of nodes) {
if (item.id === targetId) {
return currentLevel;
}
if (item.children && item.children.length > 0) {
const found = findParentLevel(item.children, targetId, currentLevel + 1);
if (found !== -1) {
return found;
}
}
}
return -1;
};
//
const parentLevel = findParentLevel(searchData, node.parentId, 0);
// + 1
return parentLevel + 1;
}; };
/**
* 获取表格数据
* @returns {Promise<void>}
*/
async function getList() {
try {
showLoading()
const { pageSize, current } = paginationState
const { success, data, total } = await apis.serviceMenu
.getNodeList({
pageSize,
current: current
})
.catch(() => {
throw new Error()
})
hideLoading()
if (config('http.code.success') === success) {
console.log("接口返回数据:", data)
listData.value = data || [];
paginationState.total = total || 0;
//
dataSource.value = convertData(listData.value);
}
} catch (error) {
hideLoading()
console.error('获取数据失败:', error);
message.error('获取数据失败');
}
}
// //
const handleDelete = async (record) => { const handleDelete = (record) => {
Modal.confirm({ Modal.confirm({
title: '确认删除', title: '确认删除',
content: `确定要删除"${record.name}"吗?${record.children && record.children.length > 0 ? '此操作将同时删除所有子节点。' : ''}`, content: `确定要删除"${record.name}"吗?${record.children && record.children.length > 0 ? '此操作将同时删除所有子节点。' : ''}`,
async onOk() { onOk() {
console.log('删除节点:', record.id); deleteNode(originalData.value, record.orgId);
try { initData();
//
const { success } = await apis.serviceMenu.delNode(record.id);
if (config('http.code.success') === success) {
message.success('删除成功'); message.success('删除成功');
//
await getList();
} else {
message.error('删除失败');
}
} catch (error) {
console.error('删除失败:', error);
message.error('删除失败');
}
} }
}); });
}; };
//
const deleteNode = (data, orgId) => {
for (let i = 0; i < data.length; i++) {
if (data[i].orgId === orgId) {
data.splice(i, 1);
return true;
}
if (data[i].subOrgList && data[i].subOrgList.length > 0) {
if (deleteNode(data[i].subOrgList, orgId)) {
return true;
}
}
}
return false;
};
// //
const showAddModal = (parent = null) => { const showAddModal = (parent = null) => {
currentParent.value = parent; currentParent.value = parent;
isEditing.value = false; isEditing.value = false;
// - //
Object.assign(formState, { Object.assign(formState, {
name: '', name: '',
status: false // projectType: 'Supervision',
status: 0
}); });
addModalVisible.value = true; addModalVisible.value = true;
}; };
@ -300,26 +338,38 @@ const handleAdd = async () => {
confirmLoading.value = true; confirmLoading.value = true;
try { try {
// - parentId //
const submitData = { await new Promise(resolve => setTimeout(resolve, 500));
companyId: 'c001',
disabled: formState.status, // falsetrue const newNode = {
id: Date.now(),
orgId: `new_${Date.now()}`,
name: formState.name, name: formState.name,
status: "", parentId: currentParent.value ? currentParent.value.orgId : '0',
parentId: currentParent.value ? currentParent.value.id : "" // 使 id path: currentParent.value ? `${currentParent.value.path}/${formState.name}` : `/0/${formState.name}`,
status: formState.status,
tenantId: "1813150290048446464",
projectType: formState.projectType,
createBy: "system",
createTime: new Date().toISOString().split('T')[0],
updateBy: "system",
updateTime: new Date().toISOString().split('T')[0],
subOrgList: [],
disabled: false
}; };
// if (currentParent.value) {
const { success } = await apis.serviceMenu.createNode(submitData); // subOrgList
addChildNode(originalData.value, currentParent.value.orgId, newNode);
} else {
//
originalData.value.push(newNode);
}
if (config('http.code.success') === success) { //
initData();
addModalVisible.value = false; addModalVisible.value = false;
message.success('添加成功'); message.success('添加成功');
//
await getList();
} else {
message.error('添加失败');
}
} catch (error) { } catch (error) {
console.error('添加失败:', error); console.error('添加失败:', error);
message.error('添加失败'); message.error('添加失败');
@ -328,12 +378,82 @@ const handleAdd = async () => {
} }
}; };
//
const addChildNode = (data, parentId, newNode) => {
for (let i = 0; i < data.length; i++) {
if (data[i].orgId === parentId) {
if (!data[i].subOrgList) {
data[i].subOrgList = [];
}
data[i].subOrgList.push(newNode);
return true;
}
if (data[i].subOrgList && data[i].subOrgList.length > 0) {
if (addChildNode(data[i].subOrgList, parentId, newNode)) {
return true;
}
}
}
return false;
};
// //
const handleCancel = () => { const handleCancel = () => {
addModalVisible.value = false; addModalVisible.value = false;
}; };
onMounted(() => { onMounted(() => {
getList(); initData();
}); });
</script> </script>
<style scoped>
.org-management {
margin: 0 auto;
padding: 10px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 0 10px;
}
.org-table {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.actions {
display: flex;
gap: 8px;
}
.header h1 {
margin: 0;
color: #1f1f1f;
font-size: 24px;
font-weight: 600;
}
:deep(.ant-table-thead > tr > th) {
background-color: #fafafa;
font-weight: 600;
}
:deep(.ant-table-row-level-0) {
font-weight: 600;
}
:deep(.ant-table-row-level-1) {
font-weight: 500;
}
:deep(.ant-table-row-level-2) {
font-weight: 400;
}
</style>

View File

@ -26,10 +26,9 @@
<a-col :span="12"> <a-col :span="12">
<a-form-item :label="'站点类型'" name="type" :required="true"> <a-form-item :label="'站点类型'" name="type" :required="true">
<a-select v-model:value="formData.type" @change="handleChange"> <a-select v-model:value="formData.type" @change="handleChange">
<a-select-option v-for="item in dicsStore.dictOptions.Station_Type" :key="item.dval" <a-select-option value="type1">社区服务中心</a-select-option>
:value="item.dval"> <a-select-option value="type2">养老服务站</a-select-option>
{{ item.introduction }} <a-select-option value="type3">综合服务中心</a-select-option>
</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
@ -46,10 +45,11 @@
<a-col :span="12"> <a-col :span="12">
<a-form-item :label="'星级等级'" name="starLevel"> <a-form-item :label="'星级等级'" name="starLevel">
<a-select v-model:value="formData.starLevel"> <a-select v-model:value="formData.starLevel">
<a-select-option v-for="item in dicsStore.dictOptions.Level" :key="item.dval" <a-select-option value="1">一星</a-select-option>
:value="item.dval"> <a-select-option value="2">二星</a-select-option>
{{ item.introduction }} <a-select-option value="3">三星</a-select-option>
</a-select-option> <a-select-option value="4">四星</a-select-option>
<a-select-option value="5">五星</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
@ -63,11 +63,12 @@
</a-card> </a-card>
<!-- 地址信息区域 --> <!-- 地址信息区域 -->
<a-card class="mb-4" title="地址信息" style="margin-top: 20px" > <a-card class="mb-4" title="地址信息">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="24"> <a-col :span="24">
<a-form-item :label="'服务中心地址'" name="address"> <a-form-item :label="'服务中心地址'" name="address">
<AreaCascader v-model:value="formData.address" @change="onAreaChange" /> <a-cascader v-model:value="formData.address" :options="addressOptions"
placeholder="请选择省/市/区" style="width: 100%"></a-cascader>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="24">
@ -91,7 +92,7 @@
</a-card> </a-card>
<!-- 站点信息区域 --> <!-- 站点信息区域 -->
<a-card class="mb-4" title="站点信息" style="margin-top: 20px"> <a-card class="mb-4" title="站点信息">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
<a-form-item :label="'建成时间'" name="buildTime"> <a-form-item :label="'建成时间'" name="buildTime">
@ -115,7 +116,7 @@
</a-card> </a-card>
<!-- 服务信息区域 --> <!-- 服务信息区域 -->
<a-card class="mb-4" title="服务信息" style="margin-top: 20px"> <a-card class="mb-4" title="服务信息">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
<a-form-item :label="'开始营业时间'" name="openTime"> <a-form-item :label="'开始营业时间'" name="openTime">
@ -129,10 +130,9 @@
<a-col :span="12"> <a-col :span="12">
<a-form-item :label="'营业状态'" name="businessStatus"> <a-form-item :label="'营业状态'" name="businessStatus">
<a-select v-model:value="formData.businessStatus"> <a-select v-model:value="formData.businessStatus">
<a-select-option v-for="item in dicsStore.dictOptions.Business_Status" :key="item.dval" <a-select-option value="open">营业中</a-select-option>
:value="item.dval"> <a-select-option value="closed">已关闭</a-select-option>
{{ item.introduction }} <a-select-option value="suspended">暂停营业</a-select-option>
</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
@ -162,7 +162,7 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<!-- <a-col :span="12"> <a-col :span="12">
<a-form-item :label="'是否有厨房'" name="hasKitchen"> <a-form-item :label="'是否有厨房'" name="hasKitchen">
<a-select v-model:value="formData.hasKitchen"> <a-select v-model:value="formData.hasKitchen">
<a-select-option value="yes"></a-select-option> <a-select-option value="yes"></a-select-option>
@ -201,12 +201,12 @@
<a-form-item :label="'日间照料中心名称'" name="daycareCenterName"> <a-form-item :label="'日间照料中心名称'" name="daycareCenterName">
<a-input v-model:value="formData.daycareCenterName" placeholder="请输入日间照料中心名称"></a-input> <a-input v-model:value="formData.daycareCenterName" placeholder="请输入日间照料中心名称"></a-input>
</a-form-item> </a-form-item>
</a-col> --> </a-col>
</a-row> </a-row>
</a-card> </a-card>
<!-- 图片上传区域 --> <!-- 图片上传区域 -->
<a-card class="mb-4" title="图片上传" style="margin-top: 20px"> <a-card class="mb-4" title="图片上传">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
<a-form-item :label="'资质附件'" name="qualificationFiles"> <a-form-item :label="'资质附件'" name="qualificationFiles">
@ -240,6 +240,11 @@
@update:open="mapVisible = $event" @update:open="mapVisible = $event"
@handleGetLng="handleLocationChange" @handleGetLng="handleLocationChange"
@select="handleLocationSelect" /> @select="handleLocationSelect" />
<div v-if="selectedLocation">
<p>经纬度: {{ selectedLocation.lnglat }}</p>
<p>地址: {{ selectedLocation.address }}</p>
<p>名称: {{ selectedLocation.placeName }}</p>
</div>
</a-modal> </a-modal>
</template> </template>
@ -255,8 +260,6 @@ import { useI18n } from 'vue-i18n'
import storage from '@/utils/storage' import storage from '@/utils/storage'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import MapPickerModal from '@/components/Map/index.vue' import MapPickerModal from '@/components/Map/index.vue'
import { useDicsStore } from '@/store'
import AreaCascader from '@/components/AreaCascader/index.vue'
const emit = defineEmits(['ok']) const emit = defineEmits(['ok'])
const { t } = useI18n() // t const { t } = useI18n() // t
const { modal, showModal, hideModal, showLoading, hideLoading } = useModal() const { modal, showModal, hideModal, showLoading, hideLoading } = useModal()
@ -375,7 +378,6 @@ const okText = ref(t('button.confirm'))
const platform = ref('') const platform = ref('')
const showMapPicker = ref(false) const showMapPicker = ref(false)
const location = ref('') const location = ref('')
const dicsStore = useDicsStore()
const handleLocationSelect = (lngLat) => { const handleLocationSelect = (lngLat) => {
console.log('确认选择:', lngLat) console.log('确认选择:', lngLat)