fix: 修改产品新增、编辑、添加详情页面
This commit is contained in:
parent
531ef16a2f
commit
93763374fe
|
@ -115,3 +115,10 @@ export const deleteProduct = (id: string) => server.patch(`/device-product/${id}
|
|||
* @returns
|
||||
*/
|
||||
export const saveProductMetadata = (data: Record<string, unknown>) => server.patch('/device-product', data)
|
||||
|
||||
/**
|
||||
* 获取设备数量
|
||||
* @param data 查询条件
|
||||
* @returns
|
||||
*/
|
||||
export const getDeviceNumber = (params:any) => server.get('/device-instance/_count', params)
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
import { ProductItem } from "@/views/device/Product/typings";
|
||||
import { defineStore } from "pinia";
|
||||
import { detail} from '@/api/device/product'
|
||||
|
||||
export const useProductStore = defineStore({
|
||||
id: 'product',
|
||||
state: () => ({
|
||||
current: {} as ProductItem | undefined
|
||||
current: {} as ProductItem | undefined,
|
||||
detail: {} as ProductItem | undefined,
|
||||
tabActiveKey: 'Info'
|
||||
}),
|
||||
actions: {
|
||||
setCurrent(current: ProductItem) {
|
||||
this.current = current
|
||||
}
|
||||
this.detail = current
|
||||
},
|
||||
async refresh(id: string) {
|
||||
const resp = await detail(id)
|
||||
if(resp.status === 200){
|
||||
this.current = resp.result
|
||||
this.detail = resp.result
|
||||
}
|
||||
},
|
||||
setTabActiveKey(key: string) {
|
||||
this.tabActiveKey = key
|
||||
},
|
||||
}
|
||||
})
|
|
@ -0,0 +1,72 @@
|
|||
<!-- 配置信息 -->
|
||||
<template>
|
||||
<a-card>
|
||||
<a-descriptions bordered>
|
||||
<template #title>
|
||||
<div style="display: flex">
|
||||
<h3>配置信息</h3>
|
||||
<div style="margin: 0 0px 0 15px; color: #1d39c4">
|
||||
<edit-outlined @click="editConfig" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<a-descriptions-item label="ID">{{
|
||||
productStore.current.id
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="产品分类">{{
|
||||
productStore.current.classifiedName
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="设备类型">{{
|
||||
productStore.current.deviceType?.text
|
||||
}}</a-descriptions-item>
|
||||
|
||||
<a-descriptions-item label="接入方式">
|
||||
<a-button type="link" @click="changeTables">{{
|
||||
productStore.current.transportProtocol
|
||||
? productStore.current.transportProtocol
|
||||
: '配置接入方式'
|
||||
}}</a-button>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">{{
|
||||
productStore.current.createTime
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="更新时间">{{
|
||||
productStore.current.modifyTime
|
||||
}}</a-descriptions-item>
|
||||
|
||||
<a-descriptions-item label="说明" :span="3">
|
||||
{{ productStore.current.describe }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
<!-- 编辑 -->
|
||||
<Save ref="saveRef" :isAdd="isAdd" :title="title" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useProductStore } from '@/store/product';
|
||||
import Save from '../../Save/index.vue';
|
||||
import {
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
PlusOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
const productStore = useProductStore();
|
||||
const saveRef = ref();
|
||||
const isAdd = ref(2);
|
||||
const title = ref('编辑');
|
||||
// console.log(productStore.current.deviceType.text, ' productStore');
|
||||
/**
|
||||
* 编辑配置信息
|
||||
*/
|
||||
const editConfig = () => {
|
||||
saveRef.value.show(productStore.current);
|
||||
};
|
||||
/**
|
||||
* 切换tabs
|
||||
*/
|
||||
const changeTables = () => {
|
||||
productStore.tabActiveKey = 'Device';
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,153 @@
|
|||
<!-- 设备接入 -->
|
||||
<template>
|
||||
<a-card>
|
||||
<div v-if="productStore.current.accessId === undefined || null">
|
||||
<a-empty :image="simpleImage">
|
||||
<template #description>
|
||||
<span>
|
||||
请先<a-button type="link" @click="showModal"
|
||||
>选择</a-button
|
||||
>设备接入网关,用以提供设备接入能力
|
||||
</span>
|
||||
</template>
|
||||
</a-empty>
|
||||
</div>
|
||||
<div v-else></div>
|
||||
</a-card>
|
||||
<!-- 选择设备 -->
|
||||
<a-modal
|
||||
title="设备接入配置"
|
||||
:visible="visible"
|
||||
width="1200px"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
@cancel="cancel"
|
||||
>
|
||||
<Search :columns="query.columns" target="deviceModal" />
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useProductStore } from '@/store/product';
|
||||
import { Empty } from 'ant-design-vue';
|
||||
import {
|
||||
getProviders,
|
||||
category,
|
||||
queryOrgThree,
|
||||
queryGatewayList,
|
||||
queryProductList,
|
||||
_deploy,
|
||||
_undeploy,
|
||||
deleteProduct,
|
||||
addProduct,
|
||||
editProduct,
|
||||
queryProductId,
|
||||
} from '@/api/device/product';
|
||||
import { isNoCommunity } from '@/utils/utils';
|
||||
const productStore = useProductStore();
|
||||
const simpleImage = ref(Empty.PRESENTED_IMAGE_SIMPLE);
|
||||
const visible = ref(false);
|
||||
const listData = ref([]);
|
||||
/**
|
||||
* 显示弹窗
|
||||
*/
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
const cancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
/**
|
||||
* 筛选
|
||||
*/
|
||||
const query = reactive({
|
||||
columns: [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
search: {
|
||||
first: true,
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '网关类型',
|
||||
key: 'accessProvider',
|
||||
dataIndex: 'accessProvider',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: async () => {
|
||||
return new Promise((res) => {
|
||||
getProviders().then((resp: any) => {
|
||||
listData.value = [];
|
||||
// const list = () => {
|
||||
if (isNoCommunity) {
|
||||
listData.value = (resp?.result || []).map(
|
||||
(item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
listData.value = (resp?.result || [])
|
||||
.filter((i: any) =>
|
||||
[
|
||||
'mqtt-server-gateway',
|
||||
'http-server-gateway',
|
||||
'mqtt-client-gateway',
|
||||
'tcp-server-gateway',
|
||||
].includes(i.id),
|
||||
)
|
||||
.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
// }
|
||||
}
|
||||
res(listData.value);
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'state',
|
||||
dataIndex: 'state',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '正常',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '禁用',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
key: 'describe',
|
||||
dataIndex: 'describe',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
width: 250,
|
||||
scopedSlots: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,179 @@
|
|||
<template>
|
||||
<page-container
|
||||
:tabList="list"
|
||||
@back="onBack"
|
||||
:tabActiveKey="productStore.active"
|
||||
@tabChange="onTabChange"
|
||||
>
|
||||
<template #title>
|
||||
<div>
|
||||
<div style="display: flex; align-items: center">
|
||||
<div>{{ productStore.current.name }}</div>
|
||||
<div style="margin: -5px 0 0 20px">
|
||||
<a-popconfirm
|
||||
title="确认禁用"
|
||||
@confirm="handleUndeploy"
|
||||
v-if="productStore.current.state === 1"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<a-switch
|
||||
:checked="productStore.current.state === 1"
|
||||
checked-children="正常"
|
||||
un-checked-children="禁用"
|
||||
/>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm
|
||||
title="确认启用"
|
||||
@confirm="handleDeploy"
|
||||
v-if="productStore.current.state === 0"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<a-switch
|
||||
:unCheckedValue="
|
||||
productStore.current.state === 0
|
||||
"
|
||||
checked-children="正常"
|
||||
un-checked-children="禁用"
|
||||
/>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding-top: 10px">
|
||||
<a-descriptions size="small" :column="4">
|
||||
<a-descriptions-item label="设备数量"
|
||||
>1</a-descriptions-item
|
||||
>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
<template #extra>
|
||||
<a-popconfirm
|
||||
title="确认应用配置"
|
||||
@confirm="handleCofig"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<a-button
|
||||
:disabled="productStore.current.state === 0"
|
||||
type="primary"
|
||||
>应用配置</a-button
|
||||
>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
<component :is="tabs[productStore.tabActiveKey]" />
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useProductStore } from '@/store/product';
|
||||
import Info from './BasicInfo/indev.vue';
|
||||
import Device from './DeviceAccess/index.vue';
|
||||
import Metadata from '../../../device/components/Metadata/index.vue';
|
||||
// import Metadata from '../../../components/Metadata/index.vue';
|
||||
import { _deploy, _undeploy, getDeviceNumber } from '@/api/device/product';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import encodeQuery from '@/utils/encodeQuery';
|
||||
|
||||
const route = useRoute();
|
||||
const checked = ref<boolean>(true);
|
||||
const productStore = useProductStore();
|
||||
const searchParams = ref({
|
||||
terms1: [
|
||||
{
|
||||
column: 'productId',
|
||||
termType: 'eq',
|
||||
value: productStore.current?.id,
|
||||
},
|
||||
],
|
||||
terms2: undefined,
|
||||
type: 'and',
|
||||
});
|
||||
|
||||
const list = [
|
||||
{
|
||||
key: 'Info',
|
||||
tab: '配置信息',
|
||||
},
|
||||
{
|
||||
key: 'Metadata',
|
||||
tab: '物模型',
|
||||
},
|
||||
{
|
||||
key: 'Device',
|
||||
tab: '设备接入',
|
||||
},
|
||||
];
|
||||
|
||||
const tabs = {
|
||||
Info,
|
||||
Metadata,
|
||||
Device,
|
||||
};
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(newId) => {
|
||||
if (newId) {
|
||||
productStore.tabActiveKey = 'Info';
|
||||
productStore.refresh(newId as string);
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
const onBack = () => {};
|
||||
|
||||
const onTabChange = (e: string) => {
|
||||
productStore.tabActiveKey = e;
|
||||
};
|
||||
|
||||
/**
|
||||
* 启用产品
|
||||
*/
|
||||
const handleDeploy = async () => {
|
||||
if (productStore.current.id) {
|
||||
const resp = await _deploy(productStore.current.id);
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
productStore.refresh(productStore.current.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 禁用产品
|
||||
*/
|
||||
const handleUndeploy = async () => {
|
||||
if (productStore.current.id) {
|
||||
const resp = await _undeploy(productStore.current.id);
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
productStore.refresh(productStore.current.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询设备数量
|
||||
*/
|
||||
const getNunmber = async () => {
|
||||
// const params = new URLSearchParams();
|
||||
// params.append('q', JSON.stringify(searchParams.value));
|
||||
// params.append('target', 'device-instance');
|
||||
// console.log(params, ' params');
|
||||
// const res = await getDeviceNumber(
|
||||
// encodeQuery({ terms: { productId: params?.id } }),
|
||||
// );
|
||||
};
|
||||
getNunmber();
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.ant-switch-loading,
|
||||
.ant-switch-disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
|
@ -22,8 +22,8 @@
|
|||
<div class="product-title">产品创建成功</div>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<div class="product-id">产品ID: 12333</div>
|
||||
<div class="product-btn" @click="jump">查看详情</div>
|
||||
<div class="product-id">产品ID: {{ idValue.value }}</div>
|
||||
<div class="product-btn" @click="showDetail">查看详情</div>
|
||||
</div>
|
||||
<div>接下来推荐操作:</div>
|
||||
<div class="product-main">1、配置产品接入方式</div>
|
||||
|
@ -47,8 +47,12 @@
|
|||
</template>
|
||||
<script lang="ts" setup name="DialogTips">
|
||||
import { getImage } from '@/utils/comm.ts';
|
||||
import { useProductStore } from '@/store/product';
|
||||
import { CheckCircleOutlined } from '@ant-design/icons-vue';
|
||||
const visible = ref<boolean>(false);
|
||||
const productStore = useProductStore();
|
||||
const router = useRouter();
|
||||
const idValue = ref({});
|
||||
/**
|
||||
* 弹窗关闭
|
||||
*/
|
||||
|
@ -58,13 +62,22 @@ const cancel = () => {
|
|||
/**
|
||||
* 显示弹窗
|
||||
*/
|
||||
const show = () => {
|
||||
const show = (id: string) => {
|
||||
visible.value = true;
|
||||
idValue.value = id;
|
||||
};
|
||||
/**
|
||||
* 查看详情
|
||||
*/
|
||||
const showDetail = () => {
|
||||
jump(idValue.value);
|
||||
};
|
||||
/**
|
||||
* 跳转页面
|
||||
*/
|
||||
const jump = () => {};
|
||||
const jump = (id: string) => {
|
||||
router.push('/iot/device/product/detail/' + id);
|
||||
};
|
||||
defineExpose({
|
||||
show: show,
|
||||
});
|
||||
|
|
|
@ -207,7 +207,7 @@ import { filterTreeSelectNode, filterSelectNode } from '@/utils/comm';
|
|||
import { FILE_UPLOAD } from '@/api/comm';
|
||||
import { isInput } from '@/utils/regular';
|
||||
import type { Rule } from 'ant-design-vue/es/form';
|
||||
import { queryProductId } from '@/api/device/product';
|
||||
import { queryProductId, addProduct, editProduct } from '@/api/device/product';
|
||||
import {
|
||||
SearchOutlined,
|
||||
CheckOutlined,
|
||||
|
@ -278,11 +278,13 @@ const validateInput = async (_rule: Rule, value: string) => {
|
|||
if (!isInput(value)) {
|
||||
return Promise.reject('请输入英文或者数字或者-或者_');
|
||||
} else {
|
||||
const res = await queryProductId(value);
|
||||
if (res.status === 200 && res.result) {
|
||||
return Promise.reject('ID重复');
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
if (props.isAdd === 1) {
|
||||
const res = await queryProductId(value);
|
||||
if (res.status === 200 && res.result) {
|
||||
return Promise.reject('ID重复');
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -355,9 +357,8 @@ const show = (data: any) => {
|
|||
form.describe = '';
|
||||
form.id = '';
|
||||
disabled.value = false;
|
||||
dialogRef.value.show();
|
||||
}
|
||||
// visible.value = true;
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -376,13 +377,34 @@ const { resetFields, validate, validateInfos, clearValidate } = useForm(
|
|||
const submitData = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(async () => {})
|
||||
.then(async () => {
|
||||
// 新增
|
||||
if (props.isAdd === 1) {
|
||||
const res = await addProduct(form);
|
||||
if (res.status === 200) {
|
||||
message.success('保存成功!');
|
||||
visible.value = false;
|
||||
dialogRef.value.show(form.id);
|
||||
} else {
|
||||
message.error('操作失败');
|
||||
}
|
||||
} else if (props.isAdd === 2) {
|
||||
// 编辑
|
||||
const res = await editProduct(form);
|
||||
if (res.status === 200) {
|
||||
message.success('保存成功!');
|
||||
visible.value = false;
|
||||
} else {
|
||||
message.error('操作失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {});
|
||||
};
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
// queryProductTree();
|
||||
queryProductTree();
|
||||
defineExpose({
|
||||
show: show,
|
||||
});
|
||||
|
|
|
@ -30,7 +30,12 @@
|
|||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<h3>{{ slotProps.name }}</h3>
|
||||
<h3
|
||||
@click.stop="handleView(slotProps.id)"
|
||||
style="font-weight: 600"
|
||||
>
|
||||
{{ slotProps.name }}
|
||||
</h3>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
|
@ -49,6 +54,8 @@
|
|||
v-if="item.popConfirm"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<a-button :disabled="item.disabled">
|
||||
<AIcon
|
||||
|
@ -96,7 +103,12 @@
|
|||
:key="i.key"
|
||||
v-bind="i.tooltip"
|
||||
>
|
||||
<a-popconfirm v-if="i.popConfirm" v-bind="i.popConfirm">
|
||||
<a-popconfirm
|
||||
v-if="i.popConfirm"
|
||||
v-bind="i.popConfirm"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<a-button
|
||||
:disabled="i.disabled"
|
||||
style="padding: 0"
|
||||
|
@ -155,6 +167,7 @@ import Save from './Save/index.vue';
|
|||
/**
|
||||
* 表格数据
|
||||
*/
|
||||
const router = useRouter();
|
||||
const isAdd = ref<number>(0);
|
||||
const title = ref<string>('');
|
||||
const statusMap = new Map();
|
||||
|
@ -238,7 +251,9 @@ const getActions = (
|
|||
title: '查看',
|
||||
},
|
||||
icon: 'EyeOutlined',
|
||||
onClick: () => {},
|
||||
onClick: () => {
|
||||
handleView(data.id);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'edit',
|
||||
|
@ -295,7 +310,7 @@ const getActions = (
|
|||
text: '删除',
|
||||
disabled: data.state !== 0,
|
||||
tooltip: {
|
||||
title: data.state !== 0 ? '已启用的设备不能删除' : '删除',
|
||||
title: data.state !== 0 ? '已启用的产品不能删除' : '删除',
|
||||
},
|
||||
popConfirm: {
|
||||
title: '确认删除?',
|
||||
|
@ -312,9 +327,14 @@ const getActions = (
|
|||
icon: 'DeleteOutlined',
|
||||
},
|
||||
];
|
||||
if (type === 'card')
|
||||
return actions.filter((i: ActionsType) => i.key !== 'view');
|
||||
return actions;
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
const add = () => {
|
||||
isAdd.value = 1;
|
||||
title.value = '新增';
|
||||
|
@ -322,6 +342,12 @@ const add = () => {
|
|||
saveRef.value.show(currentForm.value);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
const handleView = (id: string) => {
|
||||
router.push('/iot/device/product/detail/' + id);
|
||||
};
|
||||
|
||||
// 筛选
|
||||
const listData = ref([]);
|
||||
|
|
Loading…
Reference in New Issue