Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
JiangQiming 2023-03-13 15:40:45 +08:00
commit f693ba926e
19 changed files with 314 additions and 302 deletions

View File

@ -45,7 +45,6 @@ const handleCancel = () => {
emit('change', 'simple') emit('change', 'simple')
} }
const handleOk = () => { const handleOk = () => {
console.log(_value.value)
emit('update:value', _value.value) emit('update:value', _value.value)
emit('change', 'simple') emit('change', 'simple')
} }

View File

@ -185,7 +185,6 @@ const getProperty = () => {
label: item.name, label: item.name,
value: item.id, value: item.id,
})); }));
console.log(options.value)
} }
getProperty() getProperty()
</script> </script>

View File

@ -87,7 +87,6 @@ const productStore = useProductStore()
const getData = async (id?: string) => { const getData = async (id?: string) => {
const metadata = productStore.current.metadata || '{}'; const metadata = productStore.current.metadata || '{}';
console.log('metadata', metadata)
const _properties = JSON.parse(metadata).properties || [] as PropertyMetadata[] const _properties = JSON.parse(metadata).properties || [] as PropertyMetadata[]
const properties = { const properties = {
id: 'property', id: 'property',

View File

@ -96,7 +96,6 @@ const handleDelete = (index: number) => {
_value.value.splice(index, 1) _value.value.splice(index, 1)
} }
const handleClose = () => { const handleClose = () => {
console.log(editIndex.value)
editIndex.value = -1 editIndex.value = -1
} }
const handleAdd = () => { const handleAdd = () => {

View File

@ -23,8 +23,8 @@
<a-descriptions-item label="接入方式"> <a-descriptions-item label="接入方式">
<a-button type="link" @click="changeTables">{{ <a-button type="link" @click="changeTables">{{
productStore.current.transportProtocol productStore.current.accessName
? productStore.current.transportProtocol ? productStore.current.accessName
: '配置接入方式' : '配置接入方式'
}}</a-button> }}</a-button>
</a-descriptions-item> </a-descriptions-item>

View File

@ -1,24 +1,30 @@
<!-- 设备接入 --> <!-- 设备接入 -->
<template> <template>
<a-card style="min-height: 100%"> <j-card style="min-height: 100%">
<div v-if="productStore.current.accessId === undefined || null"> <div v-if="productStore.current.accessId === undefined || null">
<a-empty :image="simpleImage"> <j-empty :image="simpleImage">
<template #description> <template #description>
<span v-if="permissionStore.hasPermission('device/Product:update')"> <span
请先<a-button type="link" @click="showModal" v-if="
>选择</a-button permissionStore.hasPermission(
'device/Product:update',
)
"
>
请先<j-button type="link" @click="showModal"
>选择</j-button
>设备接入网关用以提供设备接入能力 >设备接入网关用以提供设备接入能力
</span> </span>
<span v-else>暂无权限请联系管理员</span> <span v-else>暂无权限请联系管理员</span>
</template> </template>
</a-empty> </j-empty>
</div> </div>
<div v-else> <div v-else>
<a-row :gutter="24"> <j-row :gutter="24">
<a-col :span="12"> <j-col :span="12">
<Title data="接入方式"> <Title data="接入方式">
<template #extra> <template #extra>
<a-tooltip <j-tooltip
:title=" :title="
productStore.current?.count && productStore.current?.count &&
productStore.current?.count > 0 productStore.current?.count > 0
@ -26,7 +32,7 @@
: '' : ''
" "
> >
<a-button <j-button
style="margin: 0 0 0 20px" style="margin: 0 0 0 20px"
size="small" size="small"
:disabled=" :disabled="
@ -35,9 +41,9 @@
" "
type="primary" type="primary"
@click="showDevice" @click="showDevice"
>更换</a-button >更换</j-button
> >
</a-tooltip> </j-tooltip>
</template> </template>
</Title> </Title>
<div> <div>
@ -71,13 +77,13 @@
v-for="item in access?.channelInfo?.addresses" v-for="item in access?.channelInfo?.addresses"
:key="item.address" :key="item.address"
> >
<a-badge <j-badge
:color=" :color="
item.health === -1 ? 'red' : 'green' item.health === -1 ? 'red' : 'green'
" "
:text="item.address" :text="item.address"
> >
</a-badge> </j-badge>
</div> </div>
</div> </div>
<div v-else>{{ '暂无连接信息' }}</div> <div v-else>{{ '暂无连接信息' }}</div>
@ -88,95 +94,100 @@
class="config" class="config"
> >
<template #extra> <template #extra>
<a-tooltip <j-tooltip
title="此配置来自于产品接入方式所选择的协议" title="此配置来自于产品接入方式所选择的协议"
> >
<AIcon <AIcon
type="QuestionCircleOutlined" type="QuestionCircleOutlined"
style="margin-left: 2px" style="margin-left: 2px"
/> />
</a-tooltip> </j-tooltip>
</template> </template>
</Title> </Title>
<a-form <j-form
ref="formRef" ref="formRef"
:model="formData.data" :model="formData.data"
layout="vertical" layout="vertical"
> >
<div v-for="item in metadata.properties" :key="item"> <j-form-item
<a-form-item :name="item.property"
:label="item.name" v-for="item in metadata.properties"
:rules="[ :key="item"
{ :label="item.name"
required: :rules="[
!!item?.type?.expands?.required, {
message: `${ required: !!item?.type?.expands?.required,
item.type.type === 'enum' message: `${
? '请选择' item.type.type === 'enum'
: '请输入' ? '请选择'
}${item.name}`, : '请输入'
}, }${item.name}`,
]" },
]"
>
<j-input
placeholder="请输入"
v-if="item.type.type === 'string'"
v-model:value="formData.data[item.property]"
></j-input>
<j-input-password
placeholder="请输入"
v-if="item.type.type === 'password'"
v-model:value="formData.data[item.property]"
></j-input-password>
<j-select
placeholder="请选择"
v-if="item.type.type === 'enum'"
v-model:value="formData.data[item.name]"
> >
<a-input <j-select-option
placeholder="请输入" v-for="el in item?.type?.type === 'enum' &&
v-if="item.type.type === 'string'" item?.type?.elements
v-model:value="formData.data[item.name]" ? item?.type?.elements
></a-input> : []"
<a-input-password :key="el"
placeholder="请输入" :value="el.value"
v-if="item.type.type === 'password'"
v-model:value="formData.data[item.name]"
></a-input-password>
<a-select
placeholder="请选择"
v-if="item.type.type === 'enum'"
v-model:value="formData.data[item.name]"
> >
<a-select-option {{ el.text }}
v-for="el in item?.type?.type === </j-select-option>
'enum' && item?.type?.elements </j-select>
? item?.type?.elements </j-form-item>
: []" </j-form>
:key="el"
:value="el.value"
>
{{ el.text }}
</a-select-option>
</a-select>
</a-form-item>
</div>
</a-form>
<Title data="存储策略"> <Title data="存储策略">
<template #extra> <template #extra>
<a-tooltip <j-tooltip
title="若修改存储策略,需要手动做数据迁移,平台只能搜索最新存储策略中的数据" title="若修改存储策略,需要手动做数据迁移,平台只能搜索最新存储策略中的数据"
> >
<AIcon <AIcon
type="QuestionCircleOutlined" type="QuestionCircleOutlined"
style="margin-left: 2px" style="margin-left: 2px"
/> />
</a-tooltip> </j-tooltip>
</template> </template>
</Title> </Title>
<a-form layout="vertical"> <j-form layout="vertical">
<a-form-item> <j-form-item>
<a-select <j-select
ref="select" ref="select"
v-model:value="form.storePolicy" v-model:value="form.storePolicy"
> >
<a-select-option <j-select-option
v-for="(item, index) in storageList" v-for="(item, index) in storageList"
:key="index" :key="index"
:value="item.id" :value="item.id"
>{{ item.name }}</a-select-option >{{ item.name }}</j-select-option
> >
</a-select> </j-select>
</a-form-item> </j-form-item>
</a-form> </j-form>
<PermissionButton type="primary" @click="submitDevice" hasPermission="device/Instance:update">保存</PermissionButton> <PermissionButton
</a-col> type="primary"
<a-col @click="submitDevice"
hasPermission="device/Instance:update"
>保存</PermissionButton
>
</j-col>
<j-col
:span="12" :span="12"
v-if="config?.routes && config?.routes?.length > 0" v-if="config?.routes && config?.routes?.length > 0"
> >
@ -191,7 +202,7 @@
: 'URL信息' : 'URL信息'
}} }}
</div> </div>
<a-table <j-table
:columns=" :columns="
config.id === 'MQTT' config.id === 'MQTT'
? columnsMQTT ? columnsMQTT
@ -203,14 +214,14 @@
> >
<template #bodyCell="{ text, column, record }"> <template #bodyCell="{ text, column, record }">
<template v-if="column.key === 'topic'"> <template v-if="column.key === 'topic'">
<a-tooltip <j-tooltip
placement="topLeft" placement="topLeft"
:title="text" :title="text"
> >
<div class="ellipsis-style"> <div class="ellipsis-style">
{{ text }} {{ text }}
</div> </div>
</a-tooltip> </j-tooltip>
</template> </template>
<template v-if="column.key === 'stream'"> <template v-if="column.key === 'stream'">
<div>{{ getStream(record) }}</div> <div>{{ getStream(record) }}</div>
@ -218,45 +229,45 @@
<template <template
v-if="column.key === 'description'" v-if="column.key === 'description'"
> >
<a-tooltip <j-tooltip
placement="topLeft" placement="topLeft"
:title="text" :title="text"
> >
<div class="ellipsis-style"> <div class="ellipsis-style">
{{ text }} {{ text }}
</div> </div>
</a-tooltip> </j-tooltip>
</template> </template>
<template v-if="column.key === 'address'"> <template v-if="column.key === 'address'">
<a-tooltip <j-tooltip
placement="topLeft" placement="topLeft"
:title="text" :title="text"
> >
<div class="ellipsis-style"> <div class="ellipsis-style">
{{ text }} {{ text }}
</div> </div>
</a-tooltip> </j-tooltip>
</template> </template>
<template v-if="column.key === 'example'"> <template v-if="column.key === 'example'">
<a-tooltip <j-tooltip
placement="topLeft" placement="topLeft"
:title="text" :title="text"
> >
<div class="ellipsis-style"> <div class="ellipsis-style">
{{ text }} {{ text }}
</div> </div>
</a-tooltip> </j-tooltip>
</template> </template>
</template> </template>
</a-table> </j-table>
</div> </div>
</div> </div>
</a-col> </j-col>
</a-row> </j-row>
</div> </div>
</a-card> </j-card>
<!-- 选择设备 --> <!-- 选择设备 -->
<a-modal <j-modal
title="设备接入配置" title="设备接入配置"
:visible="visible" :visible="visible"
width="1200px" width="1200px"
@ -265,16 +276,16 @@
@ok="submitData" @ok="submitData"
@cancel="cancel" @cancel="cancel"
> >
<Search <pro-search
:columns="query.columns" :columns="query.columns"
target="deviceModal" target="deviceModal"
@search="search" @search="search"
/> />
<JProTable <JProTable
:columns="columns" :columns="query.columns"
:request="queryList" :request="queryList"
ref="tableRef" ref="tableRef"
modal="card" model="CARD"
:defaultParams="{ :defaultParams="{
...temp, ...temp,
sorts: [ sorts: [
@ -288,8 +299,8 @@
:gridColumns="[2]" :gridColumns="[2]"
> >
<template #headerTitle> <template #headerTitle>
<a-button type="primary" @click="add" <j-button type="primary" @click="add"
><plus-outlined />新增</a-button ><plus-outlined />新增</j-button
> >
</template> </template>
<template #deviceType="slotProps"> <template #deviceType="slotProps">
@ -314,22 +325,35 @@
</slot> </slot>
</template> </template>
<template #content> <template #content>
<h3 style="font-weight: 600"> <Ellipsis style="width: calc(100% - 100px)">
{{ slotProps.name }} <h3 style="font-weight: 600">
</h3> {{ slotProps.name }}
<a-row> </h3>
<a-col :span="12"> </Ellipsis>
<j-row>
<j-col :span="12" v-if="slotProps.channelInfo">
<div class="card-item-content-text"> <div class="card-item-content-text">
设备类型 {{ slotProps.channelInfo?.name }}
</div> </div>
<div>直连设备</div> <div>
</a-col> {{
</a-row> slotProps.channelInfo?.addresses
? slotProps.channelInfo
?.addresses[0].address
: ''
}}
</div>
</j-col>
<j-col :span="12">
<div class="card-item-content-text">协议</div>
<div>{{ slotProps.protocolDetail?.name }}</div>
</j-col>
</j-row>
</template> </template>
</CardBox> </CardBox>
</template> </template>
<template #state="slotProps"> <template #state="slotProps">
<a-badge <j-badge
:text="slotProps.state === 1 ? '正常' : '禁用'" :text="slotProps.state === 1 ? '正常' : '禁用'"
:status="statusMap.get(slotProps.state)" :status="statusMap.get(slotProps.state)"
/> />
@ -338,13 +362,13 @@
<a>{{ slotProps.id }}</a> <a>{{ slotProps.id }}</a>
</template> </template>
</JProTable> </JProTable>
</a-modal> </j-modal>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useProductStore } from '@/store/product'; import { useProductStore } from '@/store/product';
import { ConfigMetadata } from '@/views/device/Product/typings'; import { ConfigMetadata } from '@/views/device/Product/typings';
import { Empty, message } from 'ant-design-vue'; import { Empty, FormItem, message } from 'ant-design-vue';
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import Title from '../Title/index.vue'; import Title from '../Title/index.vue';
import { usePermissionStore } from '@/store/permission'; import { usePermissionStore } from '@/store/permission';
@ -370,6 +394,9 @@ import Driver from 'driver.js';
import 'driver.js/dist/driver.min.css'; import 'driver.js/dist/driver.min.css';
import { marked } from 'marked'; import { marked } from 'marked';
import type { FormInstance, TableColumnType } from 'ant-design-vue'; import type { FormInstance, TableColumnType } from 'ant-design-vue';
import { useMenuStore } from '@/store/menu';
const formRef = ref();
const menuStore = useMenuStore();
const permissionStore = usePermissionStore(); const permissionStore = usePermissionStore();
const render = new marked.Renderer(); const render = new marked.Renderer();
marked.setOptions({ marked.setOptions({
@ -396,7 +423,6 @@ const current = ref({
name: productStore.current?.protocolName, name: productStore.current?.protocolName,
}, },
}); });
// //
const form = reactive<Record<string, any>>({ const form = reactive<Record<string, any>>({
storePolicy: 'default-row' || productStore.current?.storePolicy || '', storePolicy: 'default-row' || productStore.current?.storePolicy || '',
@ -442,15 +468,14 @@ const query = reactive({
}, },
{ {
title: '网关类型', title: '网关类型',
key: 'accessProvider', key: 'provider',
dataIndex: 'accessProvider', dataIndex: 'provider',
search: { search: {
type: 'select', type: 'select',
options: async () => { options: async () => {
return new Promise((res) => { return new Promise((res) => {
getProviders().then((resp: any) => { getProviders().then((resp: any) => {
listData.value = []; listData.value = [];
// const list = () => {
if (isNoCommunity) { if (isNoCommunity) {
listData.value = (resp?.result || []).map( listData.value = (resp?.result || []).map(
(item: any) => ({ (item: any) => ({
@ -489,19 +514,19 @@ const query = reactive({
options: [ options: [
{ {
label: '正常', label: '正常',
value: 1, value: 'enabled',
}, },
{ {
label: '禁用', label: '禁用',
value: 0, value: 'disabled',
}, },
], ],
}, },
}, },
{ {
title: '说明', title: '说明',
key: 'describe', key: 'description',
dataIndex: 'describe', dataIndex: 'description',
search: { search: {
type: 'string', type: 'string',
}, },
@ -564,9 +589,9 @@ const search = (e: any) => {
// //
const steps = [ const steps = [
{ {
element: '.device-detail-metadata', element: '#rc-tabs-0-tab-Metadata',
popover: { popover: {
className: 'driver', id: 'driver',
title: `<div id='title'>配置物模型</div><div id='guide'>1/3</div>`, title: `<div id='title'>配置物模型</div><div id='guide'>1/3</div>`,
description: `配置产品物模型,实现设备在云端的功能描述。`, description: `配置产品物模型,实现设备在云端的功能描述。`,
position: 'bottom', position: 'bottom',
@ -820,7 +845,6 @@ const getStream = (record: any) => {
* 查询接入方式 * 查询接入方式
*/ */
const queryAccessDetail = async (id: string) => { const queryAccessDetail = async (id: string) => {
console.log(id, 'id');
const res = await queryList({ const res = await queryList({
terms: [ terms: [
{ {
@ -896,7 +920,6 @@ const getProviderList = async () => {
* 提交设备数据 * 提交设备数据
*/ */
const submitData = async () => { const submitData = async () => {
console.log(current.value, 'vvv');
if (current.value) { if (current.value) {
const obj: any = { const obj: any = {
...productStore.current, ...productStore.current,
@ -938,7 +961,6 @@ const submitData = async () => {
? await updateDevice(obj) ? await updateDevice(obj)
: await saveDevice(obj); : await saveDevice(obj);
if (resp.status === 200) { if (resp.status === 200) {
console.log(productStore.current?.id, 'productStore.current?.id');
detail(productStore.current?.id || '').then((res) => { detail(productStore.current?.id || '').then((res) => {
if (res.status === 200) { if (res.status === 200) {
productStore.current = { ...res.result }; productStore.current = { ...res.result };
@ -1015,10 +1037,12 @@ const getData = async () => {
* 保存设备接入 * 保存设备接入
*/ */
const submitDevice = async () => { const submitDevice = async () => {
const res = await formRef.value.validate();
const values = { storePolicy: form.storePolicy, ...formData.data }; const values = { storePolicy: form.storePolicy, ...formData.data };
const result: any = {}; const result: any = {};
flatObj(values, result); flatObj(values, result);
const { storePolicy, ...extra } = result; const { storePolicy, ...extra } = result;
console.log({ ...extra });
const id = productStore.current?.id; const id = productStore.current?.id;
const resp = await modify(id || '', { const resp = await modify(id || '', {
id: id, id: id,
@ -1047,6 +1071,13 @@ const flatObj = (obj: any, result: any) => {
}); });
}; };
const getDetailInfo = () => {}; const getDetailInfo = () => {};
const add = () => {
const url = menuStore.hasMenu('link/AccessConfig/Detail');
if (url) {
window.open(`${origin}/#${url}`);
}
};
/** /**
* 初始化 * 初始化
*/ */

View File

@ -10,60 +10,79 @@
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<div>{{ productStore.current.name }}</div> <div>{{ productStore.current.name }}</div>
<div style="margin: -5px 0 0 20px"> <div style="margin: -5px 0 0 20px">
<a-popconfirm <j-popconfirm
title="确认禁用" title="确认禁用"
@confirm="handleUndeploy" @confirm="handleUndeploy"
v-if="productStore.current.state === 1" v-if="productStore.current.state === 1"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
> >
<a-switch <j-switch
:checked="productStore.current.state === 1" :checked="productStore.current.state === 1"
checked-children="正常" checked-children="正常"
un-checked-children="禁用" un-checked-children="禁用"
/> />
</a-popconfirm> </j-popconfirm>
<a-popconfirm <j-popconfirm
title="确认启用" title="确认启用"
@confirm="handleDeploy" @confirm="handleDeploy"
v-if="productStore.current.state === 0" v-if="productStore.current.state === 0"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
> >
<a-switch <j-switch
:unCheckedValue=" :unCheckedValue="
productStore.current.state === 0 productStore.current.state === 0
" "
checked-children="正常" checked-children="正常"
un-checked-children="禁用" un-checked-children="禁用"
/> />
</a-popconfirm> </j-popconfirm>
</div> </div>
</div> </div>
</div> </div>
<div style="padding-top: 10px"> <div style="padding-top: 10px">
<a-descriptions size="small" :column="4"> <j-descriptions size="small" :column="4">
<a-descriptions-item label="设备数量">{{ <j-descriptions-item
productStore.current?.count label="设备数量"
? productStore.current?.count style="cursor: pointer"
: 0 ><span @click="jumpDevice">{{
}}</a-descriptions-item> productStore.current?.count
</a-descriptions> ? productStore.current?.count
: 0
}}</span></j-descriptions-item
>
</j-descriptions>
</div> </div>
</template> </template>
<template #extra> <template #extra>
<a-popconfirm <!-- <j-popconfirm
title="确认应用配置" title="确认应用配置"
@confirm="handleCofig" @confirm="handleCofig"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
>
<a-button
:disabled="productStore.current.state === 0"
type="primary"
>应用配置</a-button
> >
</a-popconfirm> <j-button
:disabled="productStore.current.state === 0"
type="primary"
>应用配置</j-button
>
</j-popconfirm> -->
<PermissionButton
type="primary"
:popConfirm="{
title: `确定应用配置?`,
onConfirm: handleConfig,
}"
:disabled="productStore.current?.state === 0"
:tooltip="
productStore.current?.state === 0
? { title: '请先启用产品' }
: undefined
"
hasPermission="device/Product:update"
>应用配置</PermissionButton
>
</template> </template>
<component <component
:is="tabs[productStore.tabActiveKey]" :is="tabs[productStore.tabActiveKey]"
@ -88,6 +107,8 @@ import {
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import encodeQuery from '@/utils/encodeQuery'; import encodeQuery from '@/utils/encodeQuery';
import { useMenuStore } from '@/store/menu';
const menuStory = useMenuStore();
const route = useRoute(); const route = useRoute();
const checked = ref<boolean>(true); const checked = ref<boolean>(true);
@ -123,7 +144,7 @@ const tabs = {
Info, Info,
Metadata, Metadata,
Device, Device,
DataAnalysis DataAnalysis,
}; };
watch( watch(
@ -189,7 +210,7 @@ const handleUndeploy = async () => {
*/ */
const getProtocol = async () => { const getProtocol = async () => {
if (productStore.current?.messageProtocol) { if (productStore.current?.messageProtocol) {
const res:any = await getProtocolDetail( const res: any = await getProtocolDetail(
productStore.current?.messageProtocol, productStore.current?.messageProtocol,
); );
if (res.status === 200) { if (res.status === 200) {
@ -205,9 +226,27 @@ const getProtocol = async () => {
} }
} }
}; };
onMounted(()=>{ /**
* 详情页跳转到设备页
*/
const jumpDevice = () => {
console.log(productStore.current?.id);
const searchParams = {
column: 'productId',
termType: 'eq',
value: productStore.current?.id,
};
menuStory.jumpPage('device/Instance',{},{
target: 'device-instance',
q: JSON.stringify({ terms: [{ terms: [{searchParams}] }] }),
});
};
onMounted(() => {
getProtocol(); getProtocol();
}) if(history.state?.params?.tab){
productStore.tabActiveKey = history.state?.params?.tab
}
});
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.ant-switch-loading, .ant-switch-loading,

View File

@ -1,6 +1,6 @@
<!-- 新增编辑产品 --> <!-- 新增编辑产品 -->
<template> <template>
<a-modal <j-modal
:title="props.title" :title="props.title"
:maskClosable="false" :maskClosable="false"
destroy-on-close destroy-on-close
@ -14,18 +14,18 @@
:confirmLoading="loading" :confirmLoading="loading"
> >
<div style="margin-top: 10px"> <div style="margin-top: 10px">
<a-form <j-form
:layout="'vertical'" :layout="'vertical'"
:model="form" :model="form"
:rules="rules" :rules="rules"
ref="formRef" ref="formRef"
> >
<a-row type="flex"> <j-row type="flex">
<a-col flex="180px"> <j-col flex="180px">
<a-form-item name="photoUrl"> <j-form-item name="photoUrl">
<JUpload v-model="form.photoUrl" /> <JUpload v-model="form.photoUrl" />
</a-form-item> </j-form-item>
<!-- <a-form-item> <!-- <j-form-item>
<div class="upload-image-warp-logo"> <div class="upload-image-warp-logo">
<div class="upload-image-border-logo"> <div class="upload-image-border-logo">
<a-upload <a-upload
@ -89,37 +89,37 @@
</div> </div>
</div> </div>
</div> </div>
</a-form-item> --> </j-form-item> -->
</a-col> </j-col>
<a-col flex="auto"> <j-col flex="auto">
<a-form-item name="id"> <j-form-item name="id">
<template #label> <template #label>
<span>ID</span> <j-tooltip
<a-tooltip
title="若不填写系统将自动生成唯一ID" title="若不填写系统将自动生成唯一ID"
> >
<span>ID</span>
<AIcon <AIcon
type="QuestionCircleOutlined" type="QuestionCircleOutlined"
style="margin-left: 2px" style="margin-left: 2px"
/> />
</a-tooltip> </j-tooltip>
</template> </template>
<a-input <j-input
v-model:value="form.id" v-model:value="form.id"
placeholder="请输入ID" placeholder="请输入ID"
:disabled="disabled" :disabled="disabled"
/> />
</a-form-item> </j-form-item>
<a-form-item label="名称" name="name"> <j-form-item label="名称" name="name">
<a-input <j-input
v-model:value="form.name" v-model:value="form.name"
placeholder="请输入名称" placeholder="请输入名称"
/> />
</a-form-item> </j-form-item>
</a-col> </j-col>
</a-row> </j-row>
<a-form-item label="产品分类" name="classifiedId"> <j-form-item label="产品分类" name="classifiedId">
<a-tree-select <j-tree-select
showSearch showSearch
v-model:value="form.classifiedId" v-model:value="form.classifiedId"
placeholder="请选择产品分类" placeholder="请选择产品分类"
@ -131,40 +131,40 @@
" "
> >
<template> </template> <template> </template>
</a-tree-select> </j-tree-select>
</a-form-item> </j-form-item>
<a-form-item label="设备类型" name="deviceType"> <j-form-item label="设备类型" name="deviceType">
<a-radio-group <j-radio-group
v-model:value="form.deviceType" v-model:value="form.deviceType"
style="width: 100%" style="width: 100%"
@change="changeValue" @change="changeValue"
> >
<a-row :span="24" :gutter="10"> <j-row :span="24" :gutter="10">
<a-col <j-col
:span="8" :span="8"
v-for="item in deviceList" v-for="item in deviceList"
:key="item.value" :key="item.value"
> >
<div class="button-style"> <div class="button-style">
<a-radio-button <j-radio-button
:value="item.value" :value="item.value"
style="height: 100%; width: 100%" style="height: 100%; width: 100%"
:disabled="disabled" :disabled="disabled"
> >
<div class="card-content"> <div class="card-content">
<a-row :gutter="20"> <j-row :gutter="20">
<a-col :span="10"> <j-col :span="10">
<!-- 图片 --> <!-- 图片 -->
<div class="img-style"> <div class="img-style">
<img :src="item.logo" /> <img :src="item.logo" />
</div> </div>
</a-col> </j-col>
<a-col :span="14"> <j-col :span="14">
<span class="card-style"> <span class="card-style">
{{ item.label }} {{ item.label }}
</span> </span>
</a-col> </j-col>
</a-row> </j-row>
<!-- 勾选 --> <!-- 勾选 -->
<div <div
@ -179,21 +179,21 @@
</div> </div>
</div> </div>
</div> </div>
</a-radio-button> </j-radio-button>
</div> </div>
</a-col> </j-col>
</a-row> </j-row>
</a-radio-group> </j-radio-group>
</a-form-item> </j-form-item>
<a-form-item label="说明" name="describe"> <j-form-item label="说明" name="describe">
<a-textarea <j-textarea
v-model:value="form.describe" v-model:value="form.describe"
placeholder="请输入说明" placeholder="请输入说明"
/> />
</a-form-item> </j-form-item>
</a-form> </j-form>
</div> </div>
</a-modal> </j-modal>
<DialogTips ref="dialogRef" /> <DialogTips ref="dialogRef" />
</template> </template>
@ -275,6 +275,7 @@ const form = reactive({
*/ */
const validateInput = async (_rule: Rule, value: string) => { const validateInput = async (_rule: Rule, value: string) => {
if (value) { if (value) {
console.log(value.split('').length);
if (!isInput(value)) { if (!isInput(value)) {
return Promise.reject('请输入英文或者数字或者-或者_'); return Promise.reject('请输入英文或者数字或者-或者_');
} else { } else {
@ -302,8 +303,14 @@ const validateDeviceType = async (_rule: Rule, value: string) => {
} }
}; };
const rules = reactive({ const rules = reactive({
id: [{ validator: validateInput, trigger: 'blur' }], id: [
name: [{ required: true, message: '请输入名称', trigger: 'blur' }], { validator: validateInput, trigger: 'blur' },
{ max: 64, message: '最多可输入64位字符', trigger: 'change' },
],
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{ max: 64, message: '最多可输入64位字符', trigger: 'change' },
],
deviceType: [ deviceType: [
{ {
required: true, required: true,

View File

@ -1,6 +1,6 @@
<template> <template>
<page-container> <page-container>
<Search <pro-search
:columns="query.columns" :columns="query.columns"
target="product-manage" target="product-manage"
@search="handleSearch" @search="handleSearch"
@ -64,7 +64,7 @@
</slot> </slot>
</template> </template>
<template #content> <template #content>
<Ellipsis <Ellipsis style="width: calc(100% - 100px)"
><span ><span
@click.stop="handleView(slotProps.id)" @click.stop="handleView(slotProps.id)"
style="font-weight: 600; font-size: 16px" style="font-weight: 600; font-size: 16px"
@ -72,64 +72,30 @@
{{ slotProps.name }} {{ slotProps.name }}
</span></Ellipsis </span></Ellipsis
> >
<a-row> <j-row>
<a-col :span="12"> <j-col :span="12">
<div class="card-item-content-text"> <div class="card-item-content-text">
设备类型 设备类型
</div> </div>
<div>{{ slotProps?.deviceType?.text }}</div> <div>{{ slotProps?.deviceType?.text }}</div>
</a-col> </j-col>
<a-col :span="12"> <j-col :span="12">
<div class="card-item-content-text"> <div class="card-item-content-text">
接入方式 接入方式
</div> </div>
<Ellipsis <Ellipsis
><div> ><div>
{{ slotProps?.accessName }} {{
slotProps?.accessName
? slotProps?.accessName
: '未接入'
}}
</div></Ellipsis </div></Ellipsis
> >
</a-col> </j-col>
</a-row> </j-row>
</template> </template>
<template #actions="item"> <template #actions="item">
<!-- <a-tooltip
v-bind="item.tooltip"
:title="item.disabled && item.tooltip.title"
>
<a-popconfirm
v-if="item.popConfirm"
v-bind="item.popConfirm"
:disabled="item.disabled"
okText="确定"
cancelText="取消"
>
<a-button :disabled="item.disabled">
<AIcon
type="DeleteOutlined"
v-if="item.key === 'delete'"
/>
<template v-else>
<AIcon :type="item.icon" />
<span>{{ item?.text }}</span>
</template>
</a-button>
</a-popconfirm>
<template v-else>
<a-button
:disabled="item.disabled"
@click="item.onClick"
>
<AIcon
type="DeleteOutlined"
v-if="item.key === 'delete'"
/>
<template v-else>
<AIcon :type="item.icon" />
<span>{{ item?.text }}</span>
</template>
</a-button>
</template>
</a-tooltip> -->
<PermissionButton <PermissionButton
:disabled="item.disabled" :disabled="item.disabled"
:popConfirm="item.popConfirm" :popConfirm="item.popConfirm"
@ -152,7 +118,7 @@
</CardBox> </CardBox>
</template> </template>
<template #state="slotProps"> <template #state="slotProps">
<a-badge <j-badge
:text="slotProps.state === 1 ? '正常' : '禁用'" :text="slotProps.state === 1 ? '正常' : '禁用'"
:status="statusMap.get(slotProps.state)" :status="statusMap.get(slotProps.state)"
/> />
@ -161,39 +127,7 @@
<a>{{ slotProps.id }}</a> <a>{{ slotProps.id }}</a>
</template> </template>
<template #action="slotProps"> <template #action="slotProps">
<a-space :size="16"> <j-space :size="16">
<!-- <a-tooltip
v-for="i in getActions(slotProps)"
:key="i.key"
v-bind="i.tooltip"
>
<a-popconfirm
v-if="i.popConfirm"
v-bind="i.popConfirm"
okText="确定"
cancelText="取消"
>
<a-button
:disabled="i.disabled"
style="padding: 0"
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-popconfirm>
<a-button
style="padding: 0"
type="link"
v-else
@click="i.onClick && i.onClick(slotProps)"
>
<a-button
:disabled="i.disabled"
style="padding: 0"
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-button>
</a-tooltip> -->
<template <template
v-for="i in getActions(slotProps, 'table')" v-for="i in getActions(slotProps, 'table')"
:key="i.key" :key="i.key"
@ -212,7 +146,7 @@
<template #icon><AIcon :type="i.icon" /></template> <template #icon><AIcon :type="i.icon" /></template>
</PermissionButton> </PermissionButton>
</template> </template>
</a-space> </j-space>
</template> </template>
</JProTable> </JProTable>
<!-- 新增编辑 --> <!-- 新增编辑 -->
@ -249,11 +183,11 @@ import { omit } from 'lodash-es';
import { typeOptions } from '@/components/Search/util'; import { typeOptions } from '@/components/Search/util';
import Save from './Save/index.vue'; import Save from './Save/index.vue';
import { useMenuStore } from 'store/menu'; import { useMenuStore } from 'store/menu';
import { useRoute } from 'vue-router';
/** /**
* 表格数据 * 表格数据
*/ */
const menuStory = useMenuStore(); const menuStory = useMenuStore();
const router = useRouter();
const isAdd = ref<number>(0); const isAdd = ref<number>(0);
const title = ref<string>(''); const title = ref<string>('');
const params = ref<Record<string, any>>({}); const params = ref<Record<string, any>>({});
@ -673,6 +607,12 @@ const saveRef = ref();
const handleSearch = (e: any) => { const handleSearch = (e: any) => {
params.value = e; params.value = e;
}; };
const route = useRoute();
onMounted(() => {
if(history.state?.params?.save){
add();
}
});
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -76,8 +76,6 @@ const save = reactive({
const type = metadataStore.model.type const type = metadataStore.model.type
const _detail: ProductItem | DeviceInstance = props.type === 'device' ? instanceStore.detail : productStore.current const _detail: ProductItem | DeviceInstance = props.type === 'device' ? instanceStore.detail : productStore.current
const _metadata = JSON.parse(_detail?.metadata || '{}') const _metadata = JSON.parse(_detail?.metadata || '{}')
console.log(_metadata)
console.log(type)
const list = (_metadata[type] as any[]) || [] const list = (_metadata[type] as any[]) || []
if (formValue.id) { if (formValue.id) {
if (metadataStore.model.action === 'add' && list.some(item => item.id === formValue.id)) { if (metadataStore.model.action === 'add' && list.some(item => item.id === formValue.id)) {

View File

@ -39,7 +39,6 @@ export const validateIdName = async (_rule: Rule, val: Record<any, any>) => {
} }
export const validateValueType = async (_rule: Rule, val: Record<any, any>, title = '数据类型') => { export const validateValueType = async (_rule: Rule, val: Record<any, any>, title = '数据类型') => {
console.log(val)
if (!val) return Promise.reject(new Error('请输入元素配置')); if (!val) return Promise.reject(new Error('请输入元素配置'));
if (!val?.type) { if (!val?.type) {
return Promise.reject(new Error(`请选择${title}`)) return Promise.reject(new Error(`请选择${title}`))

View File

@ -164,7 +164,6 @@ const beforeUpload: UploadProps['beforeUpload'] = file => {
} }
const fileChange = (info: UploadChangeParam) => { const fileChange = (info: UploadChangeParam) => {
if (info.file.status === 'done') { if (info.file.status === 'done') {
console.log(info)
const { response } = info.file const { response } = info.file
if (response.status === 200) { if (response.status === 200) {
formModel.upload = response.result formModel.upload = response.result

View File

@ -3,7 +3,7 @@
<j-modal :maskClosable="false" width="1100px" :visible="true" title="选择设备" okText="确定" cancelText="取消" @ok="handleOk" <j-modal :maskClosable="false" width="1100px" :visible="true" title="选择设备" okText="确定" cancelText="取消" @ok="handleOk"
@cancel="handleCancel" :confirmLoading="btnLoading"> @cancel="handleCancel" :confirmLoading="btnLoading">
<div style="margin-top: 10px"> <div style="margin-top: 10px">
<Search :columns="columns" target="iot-card-bind-device" @search="handleSearch" type="simple" /> <pro-search :columns="columns" target="iot-card-bind-device" @search="handleSearch" type="simple" />
<j-pro-table ref="bindDeviceRef" :columns="columns" :request="queryUnbounded" model="TABLE" :defaultParams="{ <j-pro-table ref="bindDeviceRef" :columns="columns" :request="queryUnbounded" model="TABLE" :defaultParams="{
sorts: [{ name: 'createTime', order: 'desc' }], sorts: [{ name: 'createTime', order: 'desc' }],
}" :rowSelection="{ }" :rowSelection="{

View File

@ -1,7 +1,7 @@
<!-- 物联卡管理 --> <!-- 物联卡管理 -->
<template> <template>
<page-container> <page-container>
<Search :columns="columns" target="iot-card-management-search" @search="handleSearch" /> <pro-search :columns="columns" target="iot-card-management-search" @search="handleSearch" />
<j-pro-table ref="cardManageRef" :columns="columns" :request="query" <j-pro-table ref="cardManageRef" :columns="columns" :request="query"
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }" :rowSelection="{ :defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }" :rowSelection="{
selectedRowKeys: _selectedRowKeys, selectedRowKeys: _selectedRowKeys,

View File

@ -1,7 +1,7 @@
<!-- 平台对接 --> <!-- 平台对接 -->
<template> <template>
<page-container> <page-container>
<Search :columns="columns" target="platform-search" @search="handleSearch" /> <pro-search :columns="columns" target="platform-search" @search="handleSearch" />
<j-pro-table ref="platformRef" :columns="columns" :request="queryList" <j-pro-table ref="platformRef" :columns="columns" :request="queryList"
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }" :params="params" :gridColumn="3"> :defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }" :params="params" :gridColumn="3">
<template #headerTitle> <template #headerTitle>

View File

@ -1,7 +1,7 @@
<!-- 充值管理 --> <!-- 充值管理 -->
<template> <template>
<page-container> <page-container>
<Search :columns="columns" target="recharge-search" @search="handleSearch" /> <pro-search :columns="columns" target="recharge-search" @search="handleSearch" />
<j-pro-table ref="rechargeRef" :columns="columns" :request="queryRechargeList" model="TABLE" <j-pro-table ref="rechargeRef" :columns="columns" :request="queryRechargeList" model="TABLE"
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }" :params="params"> :defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }" :params="params">
<template #headerTitle> <template #headerTitle>

View File

@ -1,7 +1,7 @@
<!-- 操作记录 --> <!-- 操作记录 -->
<template> <template>
<page-container> <page-container>
<Search <pro-search
:columns="columns" :columns="columns"
target="record-search" target="record-search"
@search="handleSearch" @search="handleSearch"

View File

@ -116,9 +116,7 @@
@click="i.onClick" @click="i.onClick"
type="link" type="link"
style="padding: 0px" style="padding: 0px"
:hasPermission=" :hasPermission="'rule-engine/Instance:' + i.key"
'rule-engine/Instance:' + i.key
"
> >
<template #icon <template #icon
><AIcon :type="i.icon" ><AIcon :type="i.icon"
@ -306,11 +304,11 @@ const getActions = (
return actions; return actions;
}; };
const add = () => { const add = () => {
current.value = { (current.value = {
name:'', name: '',
description:'' description: '',
}, }),
visiable.value = true (visiable.value = true);
}; };
/** /**
* 刷新数据 * 刷新数据
@ -326,9 +324,14 @@ const openRuleEditor = (item: any) => {
`/${SystemConst.API_BASE}/rule-editor/index.html#flow/${item.id}`, `/${SystemConst.API_BASE}/rule-editor/index.html#flow/${item.id}`,
); );
}; };
const closeSave = () =>{ const closeSave = () => {
visiable.value = false; visiable.value = false;
} };
onMounted(() => {
if (history.state?.params) {
add();
}
});
</script> </script>
<style scoped> <style scoped>
</style> </style>

View File

@ -3695,8 +3695,8 @@ jetlinks-store@^0.0.3:
jetlinks-ui-components@^1.0.5: jetlinks-ui-components@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#de995a654e2613c988fac2e800b156285265c9be" resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#30de07a15481f13ea86ebc817baaab3c99034403"
integrity sha512-rZtqFmxhU9nplFqqp45bHJAoiH4zzLM8juMGQV6eCCkzquXLmDqATiAK2D/fquAcLMIY0fPgw/Dzc9odQDKmVg== integrity sha512-ULgSPU0xY6xUky3beeHVvpHyAHmT6xHsO5eS5m7a3h7AmCoxA3oTWyF20vC+K1zTJBQ7LFCouySqRRz6GimAPg==
dependencies: dependencies:
"@vueuse/core" "^9.12.0" "@vueuse/core" "^9.12.0"
ant-design-vue "^3.2.15" ant-design-vue "^3.2.15"