This commit is contained in:
2023-03-17 17:36:17 +08:00
parent c5591c7c3e
commit a66f049894
10 changed files with 1069 additions and 125 deletions

View File

@ -26,6 +26,7 @@
"fuse.js": "6.6.2", "fuse.js": "6.6.2",
"js-cookie": "3.0.1", "js-cookie": "3.0.1",
"jsencrypt": "3.3.1", "jsencrypt": "3.3.1",
"lodash-es": "^4.17.21",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"pinia": "2.0.22", "pinia": "2.0.22",
"vue": "3.2.45", "vue": "3.2.45",

View File

@ -0,0 +1,56 @@
import request from "@/utils/request";
// 查询产品列表
export function listProduct(query) {
return request({
url: "/product/product/list",
method: "get",
params: query
});
}
// 查询产品详细
export function getProduct(productId) {
return request({
url: "/product/product/" + productId,
method: "get"
});
}
// 新增产品
export function addProduct(data) {
return request({
url: "/product/product",
method: "post",
data: data
});
}
// 修改产品
export function updateProduct(data) {
return request({
url: "/product/product",
method: "put",
data: data
});
}
// 删除产品
export function delProduct(productId) {
return request({
url: "/product/product/" + productId,
method: "delete"
});
}
/**
* 检查产品SN是否唯一
* @param productSn
* @return {*}
*/
export function checkProductSn(productSn) {
return request({
url: `/product/product/check/${productSn}`
, method: "get"
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -152,7 +152,7 @@ function handleExceed() {
// 上传成功回调 // 上传成功回调
function handleUploadSuccess(res, file) { function handleUploadSuccess(res, file) {
if (res.code === 200) { if (res.code === 200) {
uploadList.value.push({ name: res.fileName, url: res.fileName }); uploadList.value.push({ name: res.fileName, url: res.url });
uploadedSuccessfully(); uploadedSuccessfully();
} else { } else {
number.value--; number.value--;

View File

@ -1,4 +1,34 @@
export const statusMap = new Map([ export const statusMap = new Map([
["0", "正常"], ["0", "正常"],
["1", "禁用"], ["1", "禁用"]
]); ]);
export const deviceTypeMap = new Map([
[1, "直连设备"],
[2, "网关子设备"],
[3, "网关子设备"]
]);
export const vertificateMethodOptions = [
{
label: "简单认证",
value: 1
}, {
label: "加密认证",
value: 2
}, {
label: "简单+加密",
value: 3
}
];
export const isTopMap = new Map([
[0, "是"],
[1, "否"]
]);
export const isMonitor = new Map([
[0, "是"],
[1, "否"]
]);

View File

@ -11,127 +11,133 @@
<el-tab-pane label="密码登录" name="password"></el-tab-pane> <el-tab-pane label="密码登录" name="password"></el-tab-pane>
<el-tab-pane label="验证码登录" name="captcha"></el-tab-pane> <el-tab-pane label="验证码登录" name="captcha"></el-tab-pane>
</el-tabs> </el-tabs>
<Transition> </Transition> <Transition name="slide-left">
<div v-if="loginMethod === 'password'"> <div v-if="loginMethod === 'password'" class="password-input">
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
v-model="loginForm.username" v-model="loginForm.username"
auto-complete="off" auto-complete="off"
placeholder="请输入用户名" placeholder="请输入用户名"
size="large" size="large"
type="text" type="text"
> >
<template #prefix> <template #prefix>
<svg-icon class="el-input__icon input-icon" icon-class="user" /> <svg-icon class="el-input__icon input-icon" icon-class="user" />
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
<el-input <el-input
v-model="loginForm.password" v-model="loginForm.password"
auto-complete="off" auto-complete="off"
placeholder="请输入密码" placeholder="请输入密码"
size="large" size="large"
type="password" type="password"
@keyup.enter="handleLogin" @keyup.enter="handleLogin"
> >
<template #prefix> <template #prefix>
<svg-icon <svg-icon
class="el-input__icon input-icon" class="el-input__icon input-icon"
icon-class="password" icon-class="password"
/> />
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item v-if="captchaEnabled" prop="code"> <el-form-item v-if="captchaEnabled" prop="code">
<el-input <el-input
v-model="loginForm.code" v-model="loginForm.code"
auto-complete="off" auto-complete="off"
placeholder="请输入验证码" placeholder="请输入验证码"
size="large" size="large"
style="width: 63%" style="width: 63%"
@keyup.enter="handleLogin" @keyup.enter="handleLogin"
> >
<template #prefix> <template #prefix>
<svg-icon <svg-icon
class="el-input__icon input-icon" class="el-input__icon input-icon"
icon-class="validCode" icon-class="validCode"
/> />
</template> </template>
</el-input> </el-input>
<div class="login-code"> <div class="login-code">
<img :src="codeUrl" class="login-code-img" @click="getCode" /> <img :src="codeUrl" class="login-code-img" @click="getCode" />
</div> </div>
</el-form-item> </el-form-item>
<el-checkbox <el-checkbox
v-model="loginForm.rememberMe" v-model="loginForm.rememberMe"
style="margin: 0px 0px 25px 0px" style="margin: 0px 0px 25px 0px"
>记住密码 >记住密码
</el-checkbox> </el-checkbox>
</div> </div>
<div v-if="loginMethod === 'captcha'"> </Transition>
<el-form-item prop="phone"> <Transition name="slide-right">
<el-input <div v-if="loginMethod === 'captcha'" class="captcha-input">
v-model="loginForm.phone" <el-form-item prop="phone">
auto-complete="off" <el-input
placeholder="请输入手机号" v-model="loginForm.phone"
size="large" auto-complete="off"
type="text" placeholder="请输入手机号"
> size="large"
<template #prefix> type="text"
<svg-icon class="el-input__icon input-icon" icon-class="user" /> >
</template> <template #prefix>
</el-input> <svg-icon class="el-input__icon input-icon" icon-class="user" />
</el-form-item> </template>
<el-form-item v-if="captchaEnabled" prop="code"> </el-input>
<el-input </el-form-item>
v-model="loginForm.code" <el-form-item v-if="captchaEnabled" prop="code">
auto-complete="off" <el-input
placeholder="请输入图形验证码" v-model="loginForm.code"
size="large" auto-complete="off"
style="width: 63%" placeholder="请输入图形验证码"
> size="large"
<!-- @keyup.enter="handleLogin"--> style="width: 63%"
<template #prefix> >
<svg-icon <!-- @keyup.enter="handleLogin"-->
class="el-input__icon input-icon" <template #prefix>
icon-class="validCode" <svg-icon
/> class="el-input__icon input-icon"
</template> icon-class="validCode"
</el-input> />
<div class="login-code"> </template>
<img :src="codeUrl" class="login-code-img" @click="getCode" /> </el-input>
</div> <div class="login-code">
</el-form-item> <img :src="codeUrl" class="login-code-img" @click="getCode" />
<el-form-item prop="smsCode"> </div>
<el-input </el-form-item>
v-model="loginForm.smsCode" <el-form-item prop="smsCode">
auto-complete="off" <el-input
class="sms-code-input" v-model="loginForm.smsCode"
placeholder="请输入短信验证码" auto-complete="off"
size="large" class="sms-code-input"
@keyup.enter="handleLogin" placeholder="请输入短信验证码"
> size="large"
<template #prefix> @keyup.enter="handleLogin"
<svg-icon class="el-input__icon input-icon" icon-class="email" /> >
</template> <template #prefix>
<template #append> <svg-icon
<el-button class="el-input__icon input-icon"
:disabled="getSMSCodeCountDown > 0" icon-class="email"
link />
type="info" </template>
@click="getSMSCode" <template #append>
> <el-button
{{ :disabled="getSMSCodeCountDown > 0"
getSMSCodeCountDown > 0 link
? `${getSMSCodeCountDown}秒后重新获取` type="info"
: "获取短信验证码" @click="getSMSCode"
}} >
</el-button> {{
</template> getSMSCodeCountDown > 0
</el-input> ? `${getSMSCodeCountDown}秒后重新获取`
</el-form-item> : "获取短信验证码"
</div> }}
</el-button>
</template>
</el-input>
</el-form-item>
</div>
</Transition>
<el-form-item style="width: 100%"> <el-form-item style="width: 100%">
<el-button <el-button
@ -408,4 +414,34 @@ getCookie();
height: 40px; height: 40px;
padding-left: 12px; padding-left: 12px;
} }
.slide-left-enter-active {
transition: all 0.3s ease-out;
}
.slide-left-leave-active {
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-left-enter-from,
.slide-left-leave-to {
transform: translateX(-500px);
position: relative;
left: -500px;
opacity: 0;
}
.slide-right-enter-active {
transition: all 0.3s ease-out;
}
.slide-right-leave-active {
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-right-enter-from,
.slide-right-leave-to {
transform: translateX(500px);
opacity: 0;
}
</style> </style>

View File

@ -177,7 +177,7 @@
children: categoryList, children: categoryList,
}, },
]" ]"
:default-expanded-keys="[0]" :default-expanded-keys="[form.parentId]"
node-key="categoryId" node-key="categoryId"
:props="{ :props="{
value: 'categoryId', value: 'categoryId',
@ -185,7 +185,6 @@
children: 'children', children: 'children',
}" }"
check-strictly check-strictly
clearable
placeholder="选择上级分类" placeholder="选择上级分类"
value-key="categoryId" value-key="categoryId"
/> />

View File

@ -0,0 +1,248 @@
<template>
<div class="app-container">
<el-tabs tab-position="left">
<el-tab-pane label="基本信息">
<el-form
ref="productRef"
:model="form"
:rules="rules"
label-width="120px"
>
<el-row :gutter="100">
<el-col :lg="12" :md="24" :sm="24" :xl="8" :xs="24">
<el-form-item label="产品名称" prop="productName">
<el-input
v-model="form.productName"
placeholder="请输入产品名称"
/>
</el-form-item>
<el-form-item label="选择产品分类" prop="categoryId">
<el-tree-select
v-model="form.categoryId"
:data="categoryOptions"
:props="{
value: 'categoryId',
label: 'name',
children: 'children',
}"
check-strictly
placeholder="请选择产品分类"
value-key="categoryId"
/>
</el-form-item>
<el-form-item label="租户ID" prop="tenantId">
<el-select v-model="form.tenantId" :remote-method="getTenantList" filterable>
<el-option v-for="item in tenantOptions" :key="item.tenantId" :label="item.tenantName"
:value="item.tenantId" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="form.remark"
placeholder="请输入内容"
type="textarea"
/>
</el-form-item>
</el-col>
<el-col :lg="12" :md="24" :sm="24" :xl="8" :xs="24">
<el-form-item label="产品编码SN" prop="productSn">
<el-input
v-model="form.productSn"
placeholder="请输入产品编码SN"
@input="checkProductSnUnique"
>
<template #suffix>
<el-icon v-if="isProductSnUnique===true" color="green">
<SuccessFilled />
</el-icon>
<el-icon v-else-if="isProductSnUnique===false" color="red">
<CircleCloseFilled />
</el-icon>
<el-icon v-else>
<InfoFilled />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="mqtt账号" prop="mqttAccount">
<el-input
v-model="form.mqttAccount"
placeholder="请输入mqtt账号"
/>
</el-form-item>
<el-form-item label="mqtt密码" prop="mqttPassword">
<el-input
v-model="form.mqttPassword"
placeholder="请输入mqtt密码"
type="password"
/>
</el-form-item>
<el-form-item label="产品秘钥" prop="mqttSecret">
<el-input
v-model="form.mqttSecret"
placeholder="请输入产品秘钥"
type="password"
/>
</el-form-item>
<el-form-item label="认证方式" prop="vertificateMethod">
<el-select v-model="form.vertificateMethod"
placeholder="请选择认证方式">
<el-option v-for="item in vertificateMethodOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :lg="12" :md="24" :sm="24" :xl="8" :xs="24">
<el-form-item label="图片地址" prop="imgUrl">
<!-- <el-input v-model="form.imgUrl" placeholder="请输入图片地址" />-->
<image-upload v-model="form.imgUrl" :limit="1"></image-upload>
</el-form-item>
</el-col>
</el-row>
<el-col :span="20">
<el-form-item style="text-align: center;margin:40px 0px;">
<el-button v-if="form.status!==2" type="primary" @click="submitForm">提交</el-button>
</el-form-item>
</el-col>
</el-form>
</el-tab-pane>
<el-tab-pane label="产品模型">
<model ref="modelRef" :model-json="form.thingsModelsJson" :product="form" />
</el-tab-pane>
<el-tab-pane label="固件管理"></el-tab-pane>
<el-tab-pane label="设备授权"></el-tab-pane>
<el-tab-pane label="告警配置"></el-tab-pane>
<el-tab-pane label="控制界面"></el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import { onMounted, reactive, ref, toRefs } from "vue";
import Model from "@/views/product/product/components/model.vue";
import { useRoute } from "vue-router";
import { addProduct, checkProductSn, getProduct, updateProduct } from "@/api/product/product";
import { listCategory } from "@/api/product/category";
import { listTenant } from "@/api/system/tenant";
import ImageUpload from "@/components/ImageUpload/index.vue";
import { ElMessage } from "element-plus";
import tab from "@/plugins/tab";
import { vertificateMethodOptions } from "@/constant/dict";
import { debounce } from "lodash-es";
const route = useRoute();
const data = reactive({
form: {},
rules: {
productName: [
{ required: true, message: "产品名称不能为空", trigger: "blur" }
],
categoryId: [
{ required: true, message: "产品分类ID不能为空", trigger: "blur" }
],
tenantId: [{ required: true, message: "租户ID不能为空", trigger: "blur" }]
}
});
const { form, rules } = toRefs(data);
const productRef = ref();
const categoryOptions = ref([]); // 产品分类树形选择
const tenantOptions = ref([]);
const isProductSnUnique = ref();
const checkProductSnUnique = debounce((sn) => {
console.log(sn);
checkProductSn(sn).then(response => {
console.log(response);
isProductSnUnique.value = response.empty;
});
}, 500);
// 表单重置
function reset() {
form.value = {
productId: null,
productSn: null,
productName: null,
categoryId: null,
tenantId: null,
mqttAccount: null,
mqttPassword: null,
mqttSecret: null,
status: null,
thingsModelsJson: null,
deviceType: null,
vertificateMethod: null,
imgUrl: null,
createBy: null,
createTime: null,
remark: null
};
// proxy.resetForm("productRef");
if (productRef.value) {
productRef.value.resetFields();
}
}
// 获取产品详情, 回显表单
const getData = () => {
reset();
const _productId = route.query.productId;
if (_productId) {
const tabObj = Object.assign({}, route, { title: "修改产品" });
tab.updatePage(tabObj);
getProduct(_productId).then((response) => {
form.value = response.data;
});
} else {
const tabObj = Object.assign({}, route, { title: "新增产品" });
tab.updatePage(tabObj);
}
};
const getCategoryList = async () => {
const response = await listCategory();
categoryOptions.value = response.data;
};
const getTenantList = async (keyword) => {
const response = await listTenant({
pageNum: 1, pageSize: 20, tenantName: keyword
});
tenantOptions.value = response.rows;
};
const submitForm = () => {
productRef.value.validate((valid) => {
if (valid) {
if (valid) {
if (form.value.productId != null) {
updateProduct(form.value).then((response) => {
ElMessage.success("修改成功");
});
} else {
addProduct(form.value).then((response) => {
ElMessage.success("新增成功");
form.value = response.data;
const tabObj = Object.assign({}, route, { title: "修改产品" });
tab.updatePage(tabObj);
});
}
}
}
});
};
onMounted(() => {
getData();
});
getCategoryList();
getTenantList();
</script>
<style scoped></style>

View File

@ -0,0 +1,139 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
v-hasPermi="['product:product:add']"
icon="plus"
plain
size="small"
type="primary"
@click="open=true"
>新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['product:product:add']"
icon="upload"
plain
size="small"
type="success"
@click=""
>新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['product:product:add']"
icon="refresh"
plain
size="small"
type="warning"
@click=""
>新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['product:product:add']"
icon="view"
plain
size="small"
type="info"
@click=""
>新增
</el-button>
</el-col>
</el-row>
<el-table :data="modelList" size="small">
<el-table-column align="center" label="名称" prop="name" />
<el-table-column align="center" label="首页显示" prop="isTop">
<template #default="{row}">
{{ isTopMap.get(row.isTop) ?? "未知" }}
</template>
</el-table-column>
<el-table-column align="center" label="实时监测" prop="isMonitor">
<template #default="{row}">
{{ isMonitor.get(row.isMonitor) ?? "未知" }}
</template>
</el-table-column>
<el-table-column align="center" label="物模型类别" prop="isMonitor">
<template #default="{row}">
<el-tag v-if="row.modelType==='events'">事件</el-tag>
<el-tag v-else-if="row.modelType==='functions'">函数</el-tag>
<el-tag v-else-if="row.modelType==='properties'">属性</el-tag>
<el-tag v-else>未知</el-tag>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改物模型对话框 -->
<el-dialog v-model="open" append-to-body title="" width="600px">
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="模型名称" prop="modelName">
<el-input v-model="form.modelName" placeholder="请输入物模型名称,例如:温度" style="width:385px;" />
</el-form-item>
<el-form-item label="标识符" prop="identifier">
<el-input v-model="form.identifier" placeholder="请输入标识符例如temperature" style="width:385px;" />
</el-form-item>
<el-form-item label="模型类别" prop="type">
<el-radio-group v-model="form.type" @change="typeChange(form.type)">
<el-radio-button label="1">属性</el-radio-button>
<el-radio-button label="2">功能</el-radio-button>
<el-radio-button label="3">事件</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-show="form.type != 3" label="首页显示" prop="isTop">
<el-switch v-model="form.isTop" :active-value="1" :inactive-value="0" active-color="#13ce66" active-text=""
inactive-text="">
</el-switch>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script setup>
import { useRoute } from "vue-router";
import { reactive, ref, toRefs, watch } from "vue";
import { isMonitor, isTopMap } from "@/constant/dict";
const props = defineProps({
product: {
type: Object,
required: true
}, modelJson: {
type: String
}
});
const { product, modelJson } = toRefs(props);
const route = useRoute();
const data = reactive({
form: {}
});
const modelList = ref([]);
const { form } = toRefs(data);
const open = ref(false); // 是否显示编辑对话框
watch(modelJson, (value) => {
if (value) {
const { functions, events, properties } = JSON.parse(value);
modelList.value = [...functions.map(el => ({
...el, modelType: "functions"
})), ...events.map(el => ({
...el, modelType: "events"
})), ...properties.map(el => ({
...el, modelType: "properties"
}))];
}
});
function getModelData() {
}
</script>
<style scoped></style>

View File

@ -0,0 +1,435 @@
<template>
<div class="app-container">
<el-form
v-show="showSearch"
ref="queryRef"
:inline="true"
:model="queryParams"
label-position="left"
label-width="100px"
>
<!-- <el-form-item label="产品编码SN" prop="productSn">-->
<!-- <el-input-->
<!-- v-model="queryParams.productSn"-->
<!-- clearable-->
<!-- placeholder="请输入产品编码SN"-->
<!-- @keyup.enter="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<el-form-item label="产品名称" prop="productName">
<el-input
v-model="queryParams.productName"
clearable
placeholder="请输入产品名称"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="产品分类ID" prop="categoryId">
<el-input
v-model="queryParams.categoryId"
clearable
placeholder="请输入产品分类ID"
@keyup.enter="handleQuery"
/>
</el-form-item>
<!-- <el-form-item label="租户ID" prop="tenantId">-->
<!-- <el-input-->
<!-- v-model="queryParams.tenantId"-->
<!-- clearable-->
<!-- placeholder="请输入租户ID"-->
<!-- @keyup.enter="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- <el-form-item label="mqtt账号" prop="mqttAccount">-->
<!-- <el-input-->
<!-- v-model="queryParams.mqttAccount"-->
<!-- clearable-->
<!-- placeholder="请输入mqtt账号"-->
<!-- @keyup.enter="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- <el-form-item label="mqtt密码" prop="mqttPassword">-->
<!-- <el-input-->
<!-- v-model="queryParams.mqttPassword"-->
<!-- clearable-->
<!-- placeholder="请输入mqtt密码"-->
<!-- @keyup.enter="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- <el-form-item label="产品秘钥" prop="mqttSecret">-->
<!-- <el-input-->
<!-- v-model="queryParams.mqttSecret"-->
<!-- clearable-->
<!-- placeholder="请输入产品秘钥"-->
<!-- @keyup.enter="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- <el-form-item label="认证方式" prop="vertificateMethod">-->
<!-- <el-input-->
<!-- v-model="queryParams.vertificateMethod"-->
<!-- clearable-->
<!-- placeholder="请输入认证方式"-->
<!-- @keyup.enter="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- <el-form-item label="图片地址" prop="imgUrl">-->
<!-- <el-input-->
<!-- v-model="queryParams.imgUrl"-->
<!-- clearable-->
<!-- placeholder="请输入图片地址"-->
<!-- @keyup.enter="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<el-form-item>
<el-button icon="Search" type="primary" @click="handleQuery"
>搜索
</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
v-hasPermi="['product:product:add']"
icon="Plus"
plain
type="primary"
@click="handleAdd"
>新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['product:product:edit']"
:disabled="single"
icon="Edit"
plain
type="success"
@click="handleUpdate"
>修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['product:product:remove']"
:disabled="multiple"
icon="Delete"
plain
type="danger"
@click="handleDelete"
>删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-hasPermi="['product:product:export']"
icon="Download"
plain
type="warning"
@click="handleExport"
>导出
</el-button>
</el-col>
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="productList"
@selection-change="handleSelectionChange"
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="产品ID" prop="productId" />
<el-table-column align="center" label="产品编码SN" prop="productSn" />
<el-table-column align="center" label="产品名称" prop="productName" />
<el-table-column align="center" label="产品分类ID" prop="categoryId" />
<el-table-column align="center" label="租户ID" prop="tenantId" />
<el-table-column align="center" label="mqtt账号" prop="mqttAccount" />
<el-table-column align="center" label="mqtt密码" prop="mqttPassword" />
<el-table-column align="center" label="产品秘钥" prop="mqttSecret" />
<el-table-column align="center" label="状态" prop="status" />
<!-- <el-table-column align="center" label="物模型JSON" prop="thingsModelsJson" />-->
<el-table-column align="center" label="设备类型" prop="deviceType" />
<el-table-column
align="center"
label="认证方式"
prop="vertificateMethod"
/>
<el-table-column align="center" label="图片地址" prop="imgUrl" />
<el-table-column align="center" label="备注" prop="remark" />
<el-table-column
align="center"
class-name="small-padding fixed-width"
label="操作"
>
<template #default="scope">
<el-button
v-hasPermi="['product:product:edit']"
icon="Edit"
link
type="primary"
@click="handleUpdate(scope.row)"
>修改
</el-button>
<el-button
v-hasPermi="['product:product:remove']"
icon="Delete"
link
type="primary"
@click="handleDelete(scope.row)"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNum"
:total="total"
@pagination="getList"
/>
<!-- 添加或修改产品对话框 -->
<el-dialog v-model="open" :title="title" append-to-body width="500px">
<el-form ref="productRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="产品编码SN" prop="productSn">
<el-input v-model="form.productSn" placeholder="请输入产品编码SN" />
</el-form-item>
<el-form-item label="产品名称" prop="productName">
<el-input v-model="form.productName" placeholder="请输入产品名称" />
</el-form-item>
<el-form-item label="产品分类ID" prop="categoryId">
<el-input v-model="form.categoryId" placeholder="请输入产品分类ID" />
</el-form-item>
<el-form-item label="租户ID" prop="tenantId">
<el-input v-model="form.tenantId" placeholder="请输入租户ID" />
</el-form-item>
<el-form-item label="mqtt账号" prop="mqttAccount">
<el-input v-model="form.mqttAccount" placeholder="请输入mqtt账号" />
</el-form-item>
<el-form-item label="mqtt密码" prop="mqttPassword">
<el-input v-model="form.mqttPassword" placeholder="请输入mqtt密码" />
</el-form-item>
<el-form-item label="产品秘钥" prop="mqttSecret">
<el-input v-model="form.mqttSecret" placeholder="请输入产品秘钥" />
</el-form-item>
<el-form-item label="认证方式" prop="vertificateMethod">
<el-input
v-model="form.vertificateMethod"
placeholder="请输入认证方式"
/>
</el-form-item>
<el-form-item label="图片地址" prop="imgUrl">
<el-input v-model="form.imgUrl" placeholder="请输入图片地址" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="form.remark"
placeholder="请输入内容"
type="textarea"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script name="Product" setup>
import { addProduct, delProduct, listProduct, updateProduct } from "@/api/product/product";
import { useRouter } from "vue-router";
const router = useRouter();
const { proxy } = getCurrentInstance();
const productList = ref([]);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref("");
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
productSn: null,
productName: null,
categoryId: null,
tenantId: null,
mqttAccount: null,
mqttPassword: null,
mqttSecret: null,
status: null,
thingsModelsJson: null,
deviceType: null,
vertificateMethod: null,
imgUrl: null
},
rules: {
productName: [
{ required: true, message: "产品名称不能为空", trigger: "blur" }
],
categoryId: [
{ required: true, message: "产品分类ID不能为空", trigger: "blur" }
],
tenantId: [{ required: true, message: "租户ID不能为空", trigger: "blur" }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询产品列表 */
function getList() {
loading.value = true;
listProduct(queryParams.value).then((response) => {
productList.value = response.rows;
total.value = response.total;
loading.value = false;
});
}
// 取消按钮
function cancel() {
open.value = false;
reset();
}
// 表单重置
function reset() {
form.value = {
productId: null,
productSn: null,
productName: null,
categoryId: null,
tenantId: null,
mqttAccount: null,
mqttPassword: null,
mqttSecret: null,
status: null,
thingsModelsJson: null,
deviceType: null,
vertificateMethod: null,
imgUrl: null,
createBy: null,
createTime: null,
remark: null
};
proxy.resetForm("productRef");
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef");
handleQuery();
}
// 多选框选中数据
function handleSelectionChange(selection) {
ids.value = selection.map((item) => item.productId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
function handleAdd() {
// reset();
// open.value = true;
// title.value = "添加产品";
router.push({
path: "/produc/product/edit"
});
}
/** 修改按钮操作 */
function handleUpdate(row) {
const _productId = row.productId;
router.push({
path: "/produc/product/edit",
query: {
productId: _productId,
pageNum: queryParams.value.pageNum
}
});
// getProduct(_productId).then(response => {
// form.value = response.data;
// open.value = true;
// title.value = "修改产品";
// });
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["productRef"].validate((valid) => {
if (valid) {
if (form.value.productId != null) {
updateProduct(form.value).then((response) => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
} else {
addProduct(form.value).then((response) => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
});
}
}
});
}
/** 删除按钮操作 */
function handleDelete(row) {
const _productIds = row.productId || ids.value;
proxy.$modal
.confirm("是否确认删除产品编号为\"" + _productIds + "\"的数据项?")
.then(function() {
return delProduct(_productIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {
});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
"product/product/export",
{
...queryParams.value
},
`product_${new Date().getTime()}.xlsx`
);
}
getList();
</script>
<style lang="scss" scoped>
.card-item {
border-radius: 15px;
}
</style>