generated from Leo_Ding/web-template
356 lines
12 KiB
Vue
356 lines
12 KiB
Vue
<template>
|
||
<x-search-bar class="mb-8-2">
|
||
<template #default="{ gutter, colSpan }">
|
||
<a-form :label-col="{ style: { width: '100px' } }" :model="searchFormData" layout="inline">
|
||
<a-row :gutter="gutter">
|
||
<a-col v-bind="colSpan">
|
||
<a-form-item label="所在区域" name="area">
|
||
<AreaCascader v-model:value="searchFormData.currentNode" @change="onAreaChange" />
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col v-bind="colSpan">
|
||
<a-form-item label="组织名称" name="name">
|
||
<a-input v-model:value="searchFormData.name" placeholder="请输入组织名称" />
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col v-bind="colSpan">
|
||
<a-form-item label="负责人" name="contactPerson">
|
||
<a-input v-model:value="searchFormData.contactPerson" placeholder="请输入负责人" />
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col v-bind="colSpan">
|
||
<a-form-item label="等级" name="level">
|
||
<a-select v-model:value="searchFormData.level">
|
||
<a-select-option v-for="item in dicsStore.dictOptions.level" :key="item.dval"
|
||
:value="item.dval">
|
||
{{ item.introduction }}
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col v-bind="colSpan">
|
||
<a-form-item label="是否中标" name="bidStatus">
|
||
<a-select v-model:value="searchFormData.bidStatus">
|
||
<a-select-option v-for="item in dicsStore.dictOptions.WinTheBidding" :key="item.dval"
|
||
:value="item.dval">
|
||
{{ item.introduction }}
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<a-row :gutter="gutter" justify="end" style="margin-top: 20px;">
|
||
<a-col>
|
||
<a-space>
|
||
<a-button @click="handleResetSearch">{{ $t('button.reset') }}</a-button>
|
||
<a-button ghost type="primary" @click="handleSearch">{{ $t('button.search') }}</a-button>
|
||
</a-space>
|
||
</a-col>
|
||
</a-row>
|
||
</a-form>
|
||
</template>
|
||
</x-search-bar>
|
||
|
||
<a-card>
|
||
<div class="title-container">
|
||
<h3>服务组织列表</h3>
|
||
<div class="title-line"></div>
|
||
</div>
|
||
<div style="margin: 20px 0;">
|
||
<a-button type="primary" @click="handleCreate">
|
||
<template #icon>
|
||
<PlusCircleOutlined />
|
||
</template>
|
||
新建
|
||
</a-button>
|
||
</div>
|
||
<a-table rowKey="id" :loading="loading" :columns="columns" :data-source="listData"
|
||
:pagination="paginationConfig" :scroll="{ x: 2200 }">
|
||
<template #bodyCell="{ column, record, index }">
|
||
<!-- 序号 -->
|
||
<template v-if="column.key === 'serialNumber'">
|
||
{{ index + 1 }}
|
||
</template>
|
||
|
||
|
||
<!-- 所在区域 -->
|
||
<template v-else-if="column.key === 'areaLabels'">
|
||
{{ record.areaLabels?.join('/') || '-' }}
|
||
</template>
|
||
|
||
<!-- 状态 -->
|
||
<template v-else-if="column.key === 'status'">
|
||
<a-tag :color="record.status === '1' ? 'processing' : 'default'">
|
||
{{ record.status === '1' ? '启用' : '停用' }}
|
||
</a-tag>
|
||
</template>
|
||
|
||
<!-- 等级(orgLv) -->
|
||
<!-- 等级 -->
|
||
<template v-else-if="column.key === 'orgLv'">
|
||
{{ getLevelText(record.orgLv) }}
|
||
</template>
|
||
|
||
<!-- 是否中标(winOrg) -->
|
||
<template v-else-if="column.key === 'winOrg'">
|
||
{{
|
||
record.winOrg
|
||
? (dicsStore.dictOptions.WinTheBidding.find(item => item.dval === record.winOrg)?.introduction ||
|
||
record.winOrg)
|
||
: '-'
|
||
}}
|
||
</template>
|
||
|
||
<!-- 创建时间 -->
|
||
<template v-else-if="column.key === 'createdAt'">
|
||
{{ formatUtcDateTime(record.created_at) }}
|
||
</template>
|
||
|
||
<!-- 操作 -->
|
||
<template v-else-if="column.key === 'actions'">
|
||
<span class="action-buttons">
|
||
<a-button type="link" size="small" @click="handleEdit(record)">编辑</a-button>
|
||
<a-divider type="vertical" />
|
||
<a-button type="link" size="small" @click="handleDetail(record)">详情</a-button>
|
||
<a-divider type="vertical" />
|
||
<a-button type="link" size="small" @click="handleToggleStatus(record)">
|
||
{{ record.status === 'enabled' ? '停用' : '启用' }}
|
||
</a-button>
|
||
<a-divider type="vertical" />
|
||
<!-- <a-button type="link" size="small" @click="handleDeviceManagement(record)">设备管理</a-button> -->
|
||
</span>
|
||
</template>
|
||
|
||
<!-- 其他字段默认显示 -->
|
||
<template v-else>
|
||
{{ record[column.dataIndex] || '-' }}
|
||
</template>
|
||
</template>
|
||
</a-table>
|
||
</a-card>
|
||
|
||
<EditDialog ref="editDialogRef" @success="handleRefresh" />
|
||
<DeviceManagementModal ref="deviceManagementModalRef" />
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
import { Modal, message, Divider } from 'ant-design-vue'
|
||
import { PlusCircleOutlined } from '@ant-design/icons-vue'
|
||
import apis from '@/apis'
|
||
import { config } from '@/config'
|
||
import { statusTypeEnum } from '@/enums/system'
|
||
import { usePagination } from '@/hooks'
|
||
import { formatUtcDateTime } from '@/utils/util'
|
||
import EditDialog from './components/EditDialog.vue'
|
||
import DeviceManagementModal from './components/AddEquipments.vue'
|
||
import { useI18n } from 'vue-i18n'
|
||
import AreaCascader from '@/components/AreaCascader/index.vue'
|
||
import { useDicsStore } from '@/store'
|
||
import dayjs from 'dayjs'
|
||
|
||
const { t } = useI18n()
|
||
const dicsStore = useDicsStore()
|
||
|
||
// 分页与数据
|
||
const {
|
||
listData,
|
||
loading,
|
||
showLoading,
|
||
hideLoading,
|
||
searchFormData,
|
||
paginationState,
|
||
resetPagination
|
||
} = usePagination()
|
||
|
||
// refs
|
||
const editDialogRef = ref(null)
|
||
const deviceManagementModalRef = ref(null)
|
||
|
||
// 表格列定义
|
||
const columns = ref([
|
||
{ title: '序号', key: 'serialNumber', width: 80 },
|
||
{ title: '机构编号', dataIndex: 'orgCode', key: 'orgCode', width: 150 },
|
||
{ title: '机构名称', dataIndex: 'name', key: 'name', width: 180 }, // ← 改为 name
|
||
{ title: '联系人', dataIndex: 'concatName', key: 'concatName', width: 120 }, // ← concatName
|
||
{ title: '联系电话', dataIndex: 'concatPhone', key: 'concatPhone', width: 150 }, // ← concatPhone
|
||
{ title: '状态', dataIndex: 'status', key: 'status', width: 100 },
|
||
{ title: '等级', dataIndex: 'orgLv', key: 'orgLv', width: 100 }, // ← orgLv
|
||
{ title: '所在区域', key: 'areaLabels', width: 200 },
|
||
{ title: '详细地址', dataIndex: 'address', key: 'address', width: 200 },
|
||
{ title: '是否中标', dataIndex: 'winOrg', key: 'winOrg', width: 100 },
|
||
{ title: '创建时间', dataIndex: 'created_at', key: 'createdAt', width: 180 }, // ← created_at
|
||
{ title: '操作', key: 'actions', width: 320, fixed: 'right' },
|
||
])
|
||
|
||
// 分页配置
|
||
const paginationConfig = computed(() => ({
|
||
current: paginationState.current,
|
||
pageSize: paginationState.pageSize,
|
||
total: paginationState.total,
|
||
showSizeChanger: true,
|
||
showQuickJumper: true,
|
||
onChange(page, pageSize) {
|
||
paginationState.current = page
|
||
paginationState.pageSize = pageSize
|
||
getList()
|
||
},
|
||
onShowSizeChange(current, size) {
|
||
paginationState.current = 1
|
||
paginationState.pageSize = size
|
||
getList()
|
||
}
|
||
}))
|
||
|
||
// 获取列表
|
||
async function getList() {
|
||
try {
|
||
showLoading()
|
||
const { pageSize, current } = paginationState
|
||
const params = {
|
||
...searchFormData.value,
|
||
pageSize,
|
||
current
|
||
}
|
||
const { success, data, total } = await apis.serviceMenu.getServiceOrgList(params)
|
||
hideLoading()
|
||
if (config('http.code.success') === success) {
|
||
listData.value = data || []
|
||
paginationState.total = total || 0
|
||
}
|
||
} catch (error) {
|
||
hideLoading()
|
||
listData.value = []
|
||
paginationState.total = 0
|
||
}
|
||
}
|
||
|
||
// 搜索
|
||
function handleSearch() {
|
||
resetPagination()
|
||
getList()
|
||
}
|
||
|
||
// 重置
|
||
function handleResetSearch() {
|
||
searchFormData.value = {}
|
||
resetPagination()
|
||
getList()
|
||
}
|
||
|
||
// 区域变化
|
||
function onAreaChange(codes, labels) {
|
||
// 如果 AreaCascader 返回的是 [codes, labels],则:
|
||
searchFormData.value.areaCodes = codes
|
||
searchFormData.value.areaLabels = labels
|
||
}
|
||
|
||
// 编辑
|
||
function handleEdit(record) {
|
||
console.log("==record===", record)
|
||
editDialogRef.value.open({ record, mode: 'edit' })
|
||
}
|
||
|
||
// 详情
|
||
function handleDetail(record) {
|
||
editDialogRef.value.open({ record, mode: 'view' })
|
||
}
|
||
|
||
// 启用/停用
|
||
function handleToggleStatus(record) {
|
||
const isEnable = record.status !== '2'; // 当前不是启用 → 要启用
|
||
const actionText = isEnable ? '启用' : '停用';
|
||
const confirmText = isEnable ? '确定要启用该组织吗?' : '确定要停用该组织吗?';
|
||
|
||
Modal.confirm({
|
||
title: `${actionText}组织`,
|
||
content: confirmText,
|
||
okText: '确定',
|
||
cancelText: '取消',
|
||
okType: isEnable ? 'primary' : 'danger',
|
||
onOk: async () => {
|
||
try {
|
||
// ✅ 正确传参:id + { status: '1' 或 '2' }
|
||
const { success } = await apis.serviceMenu.enableOrDisable(
|
||
record.id,
|
||
{ status: isEnable ? '1' : '2' }
|
||
);
|
||
|
||
if (success) {
|
||
message.success(`${actionText}成功`);
|
||
// 更新本地状态
|
||
getList()
|
||
} else {
|
||
message.error(`${actionText}失败`);
|
||
}
|
||
} catch (error) {
|
||
console.error('操作异常:', error);
|
||
message.error('操作失败');
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 设备管理
|
||
function handleDeviceManagement(record) {
|
||
if (deviceManagementModalRef.value) {
|
||
deviceManagementModalRef.value.open({ orgRecord: record })
|
||
} else {
|
||
message.error('设备管理组件未加载')
|
||
}
|
||
}
|
||
|
||
// 新建
|
||
function handleCreate() {
|
||
editDialogRef.value.open({ record: null, mode: 'create' })
|
||
}
|
||
|
||
// 刷新列表(由子组件 emit 触发)
|
||
function handleRefresh() {
|
||
getList()
|
||
}
|
||
|
||
|
||
// 等级映射函数
|
||
const getLevelText = (level) => {
|
||
if (!level) return '-'
|
||
const num = Number(level)
|
||
if (num >= 1 && num <= 5) {
|
||
return `${num}级`
|
||
}
|
||
return level // 非预期值原样返回
|
||
}
|
||
|
||
// 初始化
|
||
getList()
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.title-container {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.title-line {
|
||
height: 1px;
|
||
width: 100%;
|
||
background-color: #e8e8e8;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.action-buttons {
|
||
.ant-btn-link {
|
||
padding: 0 4px;
|
||
height: auto;
|
||
line-height: inherit;
|
||
}
|
||
|
||
.ant-divider {
|
||
margin: 0 2px;
|
||
}
|
||
}
|
||
</style> |