fix: 修改产品新增、编辑、添加详情页面

This commit is contained in:
xiongqian 2023-02-01 18:23:24 +08:00
parent 531ef16a2f
commit 93763374fe
8 changed files with 506 additions and 20 deletions

View File

@ -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)

View File

@ -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
},
}
})

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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,
});

View File

@ -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,
});

View File

@ -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([]);