iot-ui-vue/src/views/device/Product/Save/index.vue

490 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 新增编辑产品 -->
<template>
<j-modal
:title="props.title"
:maskClosable="false"
destroy-on-close
v-model:visible="visible"
@ok="submitData"
@cancel="close"
okText="确定"
cancelText="取消"
v-bind="layout"
width="650px"
:confirmLoading="loading"
>
<div style="margin-top: 10px">
<j-form
:layout="'vertical'"
:model="form"
:rules="rules"
ref="formRef"
>
<j-row type="flex">
<j-col flex="180px">
<j-form-item name="photoUrl">
<j-pro-upload
v-model="form.photoUrl"
:accept="
imageTypes && imageTypes.length
? imageTypes.toString()
: ''
"
/>
</j-form-item>
</j-col>
<j-col flex="auto">
<j-form-item name="id">
<template #label>
<j-tooltip
title="若不填写系统将自动生成唯一ID"
>
<span>ID</span>
<AIcon
type="QuestionCircleOutlined"
style="margin-left: 2px"
/>
</j-tooltip>
</template>
<j-input
v-model:value="form.id"
placeholder="请输入ID"
:disabled="disabled"
/>
</j-form-item>
<j-form-item label="名称" name="name">
<j-input
v-model:value="form.name"
placeholder="请输入名称"
/>
</j-form-item>
</j-col>
</j-row>
<j-form-item label="产品分类" name="classifiedId">
<j-tree-select
showSearch
v-model:value="form.classifiedId"
placeholder="请选择产品分类"
:tree-data="treeList"
@change="valueChange"
allow-clear
:fieldNames="{ label: 'name', value: 'id' }"
:filterTreeNode="
(v, option) => filterSelectNode(v, option)
"
>
<template> </template>
</j-tree-select>
</j-form-item>
<j-form-item label="设备类型" name="deviceType">
<j-radio-group
v-model:value="form.deviceType"
style="width: 100%"
@change="changeValue"
>
<j-row :span="24" :gutter="10">
<j-col
:span="8"
v-for="item in deviceList"
:key="item.value"
>
<div class="button-style">
<j-radio-button
:value="item.value"
style="height: 100%; width: 100%"
:disabled="disabled"
>
<div class="card-content">
<j-row :gutter="20">
<j-col :span="10">
<!-- 图片 -->
<div class="img-style">
<img :src="item.logo" />
</div>
</j-col>
<j-col :span="14">
<span class="card-style">
{{ item.label }}
</span>
</j-col>
</j-row>
<!-- 勾选 -->
<div
v-if="
form.deviceType ===
item.value
"
class="checked-icon"
>
<div>
<CheckOutlined />
</div>
</div>
</div>
</j-radio-button>
</div>
</j-col>
</j-row>
</j-radio-group>
</j-form-item>
<j-form-item label="说明" name="description">
<j-textarea
:maxlength="200"
showCount
:auto-size="{ minRows: 4, maxRows: 5 }"
v-model:value="form.describe"
placeholder="请输入说明"
/>
</j-form-item>
</j-form>
</div>
</j-modal>
<DialogTips ref="dialogRef" />
</template>
<script lang="ts" setup>
import { category } from '@/api/device/product';
import { Form } from 'ant-design-vue';
import { getImage } from '@/utils/comm.ts';
import { message } from 'ant-design-vue';
import DialogTips from '../DialogTips/index.vue';
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, addProduct, editProduct } from '@/api/device/product';
import {
SearchOutlined,
CheckOutlined,
DeleteOutlined,
} from '@ant-design/icons-vue';
const emit = defineEmits(['success']);
const props = defineProps({
title: {
type: String,
defult: '',
},
isAdd: {
type: Number,
default: 0,
},
});
const loading = ref<boolean>(false);
const dialogRef = ref();
const treeList = ref<Record<string, any>[]>([]);
const visible = ref<boolean>(false);
const logoLoading = ref<boolean>(false);
const formRef = ref();
const disabled = ref<boolean>(false);
const useForm = Form.useForm;
const _selectedRowKeys = ref([]);
const photoValue = ref('/images/device-product.png');
const imageTypes = reactive([
'image/jpeg',
'image/png',
'image/jpg',
'image/jfif',
'image/pjp',
'image/pjpeg',
]);
const deviceList = ref([
{
label: '直连设备',
value: 'device',
logo: getImage('/device-type-1.png'),
},
{
label: '网关子设备',
value: 'childrenDevice',
logo: getImage('/device-type-2.png'),
},
{
label: '网关设备',
value: 'gateway',
logo: getImage('/device/device-type-3.png'),
},
]);
const form = reactive({
id: undefined,
name: '',
classifiedId: '',
classifiedName: '',
deviceType: '',
describe: undefined,
photoUrl: getImage('/device/instance/device-card.png'),
});
/**
* 校验id
*/
const validateInput = async (_rule: Rule, value: string) => {
if (value) {
console.log(value.split('').length);
if (!isInput(value)) {
return Promise.reject('请输入英文或者数字或者-或者_');
} else {
if (props.isAdd === 1) {
const res = await queryProductId(value);
if (res.status === 200 && res.result) {
return Promise.reject('ID重复');
} else {
return Promise.resolve();
}
}
}
} else {
return Promise.resolve();
}
};
/**
* 校验是否选择设备类型
*/
const validateDeviceType = async (_rule: Rule, value: string) => {
if (!value) {
return Promise.reject('请选择设备类型');
} else {
return Promise.resolve();
}
};
const rules = reactive({
id: [
{ validator: validateInput, trigger: 'blur' },
{ max: 64, message: '最多可输入64位字符', trigger: 'change' },
],
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{ max: 64, message: '最多可输入64位字符', trigger: 'change' },
],
deviceType: [
{
required: true,
validator: validateDeviceType,
trigger: 'blur',
},
],
description: [
{ max: 200, message: '最多可输入200位字符', trigger: 'blur' },
],
});
const valueChange = (value: string, label: string) => {
form.classifiedName = label[0];
};
/**
* 查询产品分类
*/
const queryProductTree = async () => {
category({
paging: false,
}).then((resp) => {
if (resp.status === 200) {
treeList.value = resp.result;
}
});
};
watch(
() => props.isAdd,
() => {
// queryProductTree();
},
{ immediate: true, deep: true },
);
/**
* 显示弹窗
*/
const show = (data: any) => {
if (props.isAdd === 2) {
form.name = data.name;
form.classifiedId = data.classifiedId;
form.classifiedName = data.classifiedName;
form.photoUrl = data.photoUrl || photoValue.value;
form.deviceType = data.deviceType.value;
form.describe = form.describe;
form.id = data.id;
disabled.value = true;
} else if (props.isAdd === 1) {
form.name = '';
form.classifiedId = '';
form.classifiedName = '';
form.photoUrl = getImage('/device/instance/device-card.png');
form.deviceType = '';
form.describe = undefined;
form.id = undefined;
disabled.value = false;
}
visible.value = true;
};
/**
* 关闭弹窗
*/
const close = () => {
visible.value = false;
};
const { resetFields, validate, validateInfos, clearValidate } = useForm(
form,
rules,
);
/**
* 提交表单数据
*/
const submitData = () => {
formRef.value
.validate()
.then(async () => {
// 新增
if (props.isAdd === 1) {
if (form.id === '') {
form.id = undefined;
}
const res = await addProduct(form);
if (res.status === 200) {
message.success('保存成功!');
visible.value = false;
emit('success');
dialogRef.value.show(res.result.id);
} else {
message.error('操作失败');
}
} else if (props.isAdd === 2) {
// 编辑
const res = await editProduct(form);
if (res.status === 200) {
message.success('保存成功!');
emit('success');
visible.value = false;
} else {
message.error('操作失败');
}
}
})
.catch((err: any) => {});
};
/**
* 初始化
*/
queryProductTree();
defineExpose({
show: show,
});
</script>
<style scoped lang="less">
.card-style {
position: relative;
top: 19px;
}
.upload-image-warp-logo {
display: flex;
justify-content: flex-start;
.upload-image-border-logo {
position: relative;
overflow: hidden;
border: 1px dashed #d9d9d9;
transition: all 0.3s;
width: 160px;
height: 150px;
&:hover {
border: 1px dashed #1890ff;
display: flex;
}
.upload-image-content-logo {
align-items: center;
justify-content: center;
position: relative;
display: flex;
flex-direction: column;
width: 160px;
height: 150px;
padding: 8px;
background-color: rgba(0, 0, 0, 0.06);
cursor: pointer;
.loading-logo {
position: absolute;
top: 50%;
}
.loading-icon {
position: absolute;
}
.upload-image {
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-position: 50%;
background-size: cover;
}
.upload-image-icon {
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-position: 50%;
background-size: inherit;
}
.upload-image-mask {
align-items: center;
justify-content: center;
position: absolute;
top: 0;
left: 0;
display: none;
width: 100%;
height: 100%;
color: #fff;
font-size: 16px;
background-color: rgba(0, 0, 0, 0.35);
}
&:hover .upload-image-mask {
display: flex;
}
}
}
}
.button-style {
background-color: #fff;
height: 66px;
overflow: hidden;
.card-content {
width: 100%;
.img-style {
position: relative;
top: 16px;
}
.checked-icon {
position: absolute;
right: -22px;
bottom: -22px;
z-index: 2;
width: 44px;
height: 44px;
color: #fff;
background-color: @primary-color-active;
transform: rotate(-45deg);
> div {
position: relative;
height: 100%;
transform: rotate(45deg);
> span {
position: absolute;
top: 6px;
left: 6px;
font-size: 12px;
}
}
}
&.checked {
position: relative;
color: @primary-color-active;
border-color: @primary-color-active;
> .checked-icon {
display: block;
}
}
}
}
</style>