yukai 2 år sedan
förälder
incheckning
99345ed54b

+ 19 - 0
src/api/ota/index.ts

@@ -0,0 +1,19 @@
+import { get, post, del, put, file } from '/@/utils/request';
+import getOrigin from '/@/utils/origin';
+const baseUrl = getOrigin(import.meta.env.VITE_SERVER_URL);
+
+export default {
+  manage: {
+    getList: (data: any) => get('/dev_ota_fireware/list', data),
+    del: (ids: number) => del('/dev_ota_fireware/delete', { ids }),
+    add: (data: any) => post('/dev_ota_fireware/add', data),
+    edit: (data: any) => post('/dev_ota_fireware/edit', data),
+  },
+  module: {
+    getSubList: () => get('/product/list'),
+    getList: (data: any) => get('/dev_ota_module/list', data),
+    del: (ids: number) => del('/dev_ota_module/delete', { ids }),
+    add: (data: any) => post('/dev_ota_module/add', data),
+    edit: (data: any) => post('/dev_ota_module/edit', data),
+  }
+} 

+ 113 - 0
src/views/iot/ota-update/module/edit.vue

@@ -0,0 +1,113 @@
+<template>
+	<el-dialog class="api-edit" v-model="showDialog" :title="`${formData.id ? '编辑模块' : '新增模块'}`" width="600px"
+		:close-on-click-modal="false" :close-on-press-escape="false">
+		<el-form ref="formRef" :model="formData" :rules="ruleForm" label-width="80px">
+
+
+		
+			<el-form-item label="所属产品" prop="productId">
+				<el-select v-model="formData.productId" filterable placeholder="请选择产品">
+					<el-option v-for="item in productData" :key="item.id" :label="item.name" :value="item.id.toString()" value-key="id">
+					</el-option>
+				</el-select>
+			</el-form-item>
+			<el-form-item label="模块名称" prop="name">
+				<el-input v-model="formData.name" placeholder="请输入模块名称" />
+			</el-form-item>
+			
+			<el-form-item label="模块别名" prop="nameAs">
+				<el-input v-model="formData.nameAs" placeholder="请输入模块别名" />
+			</el-form-item>
+
+			
+			<el-form-item label="模块描述" prop="describe">
+				<el-input v-model="formData.describe" type="textarea" :rows="3" />
+			</el-form-item>
+
+		</el-form>
+		<template #footer>
+			<div class="dialog-footer">
+				<el-button @click="showDialog = false">取消</el-button>
+				<el-button type="primary" @click="onSubmit">确定</el-button>
+			</div>
+		</template>
+	</el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, nextTick } from 'vue';
+import api from '/@/api/ota';
+import { ruleRequired } from '/@/utils/validator';
+import { ElMessage } from 'element-plus';
+import uploadFile from '/@/components/upload/uploadFile.vue'
+
+
+const emit = defineEmits(['getList']);
+
+const showDialog = ref(false);
+const formRef = ref();
+const productData=ref([{
+	'id':'',
+	'name':'',
+}]);
+
+const baseForm = {
+	id: undefined,
+	name: '',
+	productId: 0,
+	nameAs: '',
+	describe: '',
+};
+
+const formData = reactive({
+	...baseForm,
+});
+
+const ruleForm = {
+	name: [ruleRequired('模块名称不能为空')],
+	productId: [ruleRequired('所属产品不能为空')],
+};
+
+const onSubmit = async () => {
+	await formRef.value.validate();
+
+	const theApi = formData.id ? api.module.edit : api.module.add;
+
+	await theApi(formData);
+
+	ElMessage.success('操作成功');
+	resetForm();
+	showDialog.value = false;
+	emit('getList');
+};
+
+const resetForm = async () => {
+	Object.assign(formData, { ...baseForm });
+	formRef.value && formRef.value.resetFields();
+};
+const getProductList = () => {
+			api.module.getSubList().then((res: any) => {
+				let productDataList = res.product;
+				productData.value = productDataList;
+	
+			});
+		};
+const open = async (row: any) => {
+	getProductList();
+
+	resetForm();
+	showDialog.value = true;
+	nextTick(() => {
+		Object.assign(formData, { ...row });
+	});
+};
+
+defineExpose({ open });
+</script>
+<style>
+.vertical-form-item {
+	display: block;
+	margin-bottom: 10px;
+	/* 可选,设置间距 */
+}
+</style>

+ 107 - 0
src/views/iot/ota-update/module/index.vue

@@ -0,0 +1,107 @@
+<template>
+	<el-card shadow="hover">
+		<div class="search">
+			<el-form :inline="true" ref="queryRef">
+				<el-form-item label="模块名称:" prop="name">
+					<el-input
+						v-model="params.keyWord"
+						placeholder="请输入产品名称"
+						clearable
+						size="default"
+						style="width: 240px"
+						@keyup.enter.native="getList"
+					/>
+				</el-form-item>
+				<el-form-item label="所属产品" prop="productId">
+					<el-select v-model="params.productId" filterable placeholder="请选择产品">
+						<el-option v-for="item in productData" :key="item.id" :label="item.name" :value="item.id.toString()" value-key="id"> </el-option>
+					</el-select>
+				</el-form-item>
+				<el-form-item>
+					<el-button size="default" type="primary" class="ml10" @click="getList()">
+						<el-icon>
+							<ele-Search />
+						</el-icon>
+						查询
+					</el-button>
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" v-auth="'add'" @click="addOrEdit()">
+						<el-icon>
+							<ele-FolderAdd />
+						</el-icon>
+						添加模块
+					</el-button>
+				</el-form-item>
+			</el-form>
+		</div>
+		<el-table :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
+			<el-table-column prop="id" label="ID" width="60" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="name" label="模块名称" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="nameAs" label="模块别名" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="ProductName" label="所属产品" show-overflow-tooltip></el-table-column>
+
+			<el-table-column prop="createdAt" label="创建时间" min-width="100" align="center"></el-table-column>
+			<el-table-column label="操作" width="200" align="center">
+				<template #default="scope">
+					<el-button size="small" text type="warning" v-auth="'edit'" @click="addOrEdit(scope.row)">编辑</el-button>
+					<el-button size="small" text type="danger" v-auth="'del'" @click="del(scope.row)">删除</el-button>
+				</template>
+			</el-table-column>
+		</el-table>
+		<pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize" @pagination="getList()" />
+		<EditForm ref="editFormRef" @getList="getList()"></EditForm>
+	</el-card>
+</template>
+  
+<script lang="ts" setup>
+import api from '/@/api/ota'
+import { useSearch } from '/@/hooks/useCommon'
+import { ElMessageBox, ElMessage } from 'element-plus'
+import EditForm from './edit.vue'
+import { ref } from 'vue'
+
+const queryRef = ref()
+const productData = ref([
+	{
+		id: '',
+		name: '',
+	},
+])
+const editFormRef = ref()
+
+const { params, tableData, getList, loading } = useSearch<any[]>(api.module.getList, 'Data', { keyWord: '' })
+
+getList()
+const getProductList = () => {
+	api.module.getSubList().then((res: any) => {
+		let productDataList = res.product
+		productData.value = productDataList
+	})
+}
+getProductList()
+
+const addOrEdit = async (row?: any) => {
+	if (row) {
+		editFormRef.value.open(row)
+		return
+	} else {
+		editFormRef.value.open()
+	}
+}
+
+
+
+const del = (row: any) => {
+	ElMessageBox.confirm(`此操作将删除模型:“${row.name}”,是否继续?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+	}).then(async () => {
+		await api.module.del(row.id)
+		ElMessage.success('删除成功')
+		getList()
+	})
+}
+</script>
+  

+ 216 - 0
src/views/iot/ota-update/update/check.vue

@@ -0,0 +1,216 @@
+<template>
+	<el-dialog
+		class="api-edit"
+		v-model="showDialog"
+		:title="`${formData.id ? '编辑升级包' : '新增升级包'}`"
+		width="768px"
+		:close-on-click-modal="false"
+		:close-on-press-escape="false"
+	>
+		<el-form ref="formRef" :model="formData" :rules="ruleForm" label-width="160px">
+			<el-form-item label="升级包类型" prop="typo">
+				<el-radio-group v-model="formData.typo">
+					<el-radio label="1">整包</el-radio>
+					<el-radio label="2">差分</el-radio>
+				</el-radio-group>
+			</el-form-item>
+
+			<el-form-item label="升级包名称" prop="name">
+				<el-input v-model="formData.name" placeholder="请输入升级包名称" />
+			</el-form-item>
+			<el-form-item label="所属产品" prop="productId">
+				<el-select v-model="formData.productId" filterable placeholder="请选择产品" @change="seletChange">
+					<el-option v-for="item in productData" :key="item.id" :label="item.name" :value="item.id.toString()" value-key="id"> </el-option>
+				</el-select>
+			</el-form-item>
+			<div>
+				<el-form-item label="升级包模块" prop="module">
+					<el-select v-model="formData.module" filterable placeholder="请选择产品">
+						<el-option v-for="item in moduleData" :key="item.id" :label="item.name" :value="item.id.toString()" value-key="id"> </el-option>
+				</el-select>
+				</el-form-item>
+			</div>
+			<el-form-item label="升级包版本号" prop="version" v-if="formData.typo == 1">
+				<el-input v-model="formData.version" placeholder="请输入待升级包版本号" />
+			</el-form-item>
+
+			<el-form-item label="待升级版本号" prop="waitVersion" v-if="formData.typo == 2">
+				<el-input v-model="formData.waitVersion" placeholder="请输入待升级版本号" />
+			</el-form-item>
+
+			<el-form-item label="升级后版本号" prop="afterVersion" v-if="formData.typo == 2">
+				<el-input v-model="formData.afterVersion" placeholder="请输入升级后版本号" />
+			</el-form-item>
+
+			<el-form-item label="算法签名" prop="are">
+				<el-select v-model="formData.are" filterable placeholder="请选择算法签名">
+					<el-option v-for="item in areType" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+				</el-select>
+			</el-form-item>
+
+			<el-form-item label="选择升级包" prop="url">
+				<el-upload :accept="['.bin','.tar','.gz','.tar.xz','.zip','.gzip','.apk','.dav','.pack']" :show-file-list="false" :limit="1" :headers="headers" :action="uploadUrl" :on-success="updateImg">
+				
+					<el-button type="Default">上传升级包</el-button>
+				</el-upload>
+			</el-form-item>
+
+			<el-form-item label="升级包是否验证" prop="check">
+				<el-radio-group v-model="formData.check">
+					<el-radio :label="1">是</el-radio>
+					<el-radio :label="2">否</el-radio>
+				</el-radio-group>
+			</el-form-item>
+			<el-form-item label="升级包描述" prop="describe">
+				<el-input v-model="formData.describe" type="textarea" :rows="3" />
+			</el-form-item>
+
+			<el-form-item label="推送设备的自定义消息" prop="info">
+				<el-input v-model="formData.info" type="textarea" :rows="3" />
+			</el-form-item>
+		</el-form>
+		<template #footer>
+			<div class="dialog-footer">
+				<el-button @click="showDialog = false">取消</el-button>
+				<el-button type="primary" @click="onSubmit">确定</el-button>
+			</div>
+		</template>
+	</el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, nextTick } from 'vue'
+import api from '/@/api/ota'
+import { ruleRequired } from '/@/utils/validator'
+import { ElMessage } from 'element-plus'
+import getOrigin from '/@/utils/origin';
+
+
+const emit = defineEmits(['getList'])
+
+const showDialog = ref(false)
+const formRef = ref()
+const uploadUrl: string = getOrigin(import.meta.env.VITE_API_URL + "/dev_ota_fireware/upload");
+		const headers = {
+			Authorization: 'Bearer ' + localStorage.token,
+		};
+const areType = ref([
+	{
+		label: 'MD5',
+		value: 'MD5',
+	},
+	{
+		label: 'SHA256',
+		value: 'SHA256',
+	},
+])
+const productData = ref([
+	{
+		id: '',
+		name: '',
+	},
+])
+const moduleData = ref([
+	{
+		id: '',
+		name: '',
+	},
+])
+const baseForm = {
+	id: undefined,
+	typo: '1',
+	name: '',
+	productId: '',
+	module: '',
+	version: '',
+	are: 'MD5',
+	url: '',
+	check: 1,
+	describe: '',
+	info: '',
+	afterVersion: '',
+	waitVersion: '',
+}
+
+const formData = reactive({
+	...baseForm,
+})
+
+const ruleForm = {
+	typo: [ruleRequired('请选择升级包类型')],
+	name: [ruleRequired('升级包名称不能为空')],
+	productId: [ruleRequired('请选择所属产品')],
+	module: [ruleRequired('请选择升级包模块')],
+	version: [ruleRequired('升级包版本号不能为空')],
+	are: [ruleRequired('算法签名不能为空')],
+	check: [ruleRequired('升级包是否验证不能为空')],
+	// url: [ruleRequired('升级包不能为空')],
+	afterVersion: [ruleRequired('待升级版本号不能为空')],
+	waitVersion: [ruleRequired('升级后版本号不能为空')],
+}
+
+const updateImg = (res: any) => {
+	console.log(res);
+
+			if (res.code === 0) {
+				ElMessage.success('上传成功');
+			} else {
+				ElMessage.error(res.message);
+			}
+		};
+
+const onSubmit = async () => {
+	await formRef.value.validate()
+
+	const theApi = formData.id ? api.manage.edit : api.manage.add
+
+	await theApi(formData)
+
+	ElMessage.success('操作成功')
+	resetForm()
+	showDialog.value = false
+	emit('getList')
+}
+
+const seletChange = (val: Number) => {
+	formData.module='';
+	getModuleList(val);
+};
+
+const getModuleList = (productID: Number) => {
+	api.module.getList({productID:productID}).then((res: any) => {
+		let productDataList = res.Data
+		moduleData.value = productDataList
+	})
+}
+
+const getProductList = () => {
+	api.module.getSubList().then((res: any) => {
+		let productDataList = res.product
+		productData.value = productDataList
+	})
+}
+getProductList()
+
+const resetForm = async () => {
+	Object.assign(formData, { ...baseForm })
+	formRef.value && formRef.value.resetFields()
+}
+
+const open = async (row: any) => {
+	resetForm()
+	showDialog.value = true
+	nextTick(() => {
+		Object.assign(formData, { ...row })
+	})
+}
+
+defineExpose({ open })
+</script>
+<style>
+.vertical-form-item {
+	display: block;
+	margin-bottom: 10px;
+	/* 可选,设置间距 */
+}
+</style>

+ 124 - 0
src/views/iot/ota-update/update/component/batch.vue

@@ -0,0 +1,124 @@
+
+<template>
+	<el-card shadow="hover">
+		<div class="search">
+			<el-form :inline="true" ref="queryRef">
+				<el-form-item label="批次名称:" prop="name">
+					<el-input v-model="params.keyWord" placeholder="请输入产品名称" clearable size="default" style="width: 240px"
+						@keyup.enter.native="getList" />
+				</el-form-item>
+		
+				<el-form-item>
+
+					<el-button size="default" type="primary" class="ml10" @click="getList()">
+						<el-icon>
+							<ele-Search />
+						</el-icon>
+						查询
+					</el-button>
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" v-auth="'add'" @click="addOrEdit()">
+						<el-icon>
+							<ele-FolderAdd />
+						</el-icon>
+						添加升级包
+					</el-button>
+
+				</el-form-item>
+			</el-form>
+		</div>
+		<el-table :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
+			<el-table-column prop="id" label="ID" width="60" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="name" label="升级包名称" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="typo" label="类型" show-overflow-tooltip>
+				<template #default="scope">
+					<el-tag  size="small" v-if="scope.row.typo==1">整包</el-tag>
+					<el-tag  type="info" size="small" v-if="scope.row.typo==2">差分</el-tag>
+				</template>
+			</el-table-column>
+			<el-table-column prop="productName" label="所属产品" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="moduleName" label="模块名称" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="name" label="状态" width="100" align="center">
+				<template #default="scope">
+					<el-tag type="success" size="small" v-if="scope.row.status">验证</el-tag>
+					<el-tag type="info" size="small" v-else>未验证</el-tag>
+				</template>
+			</el-table-column> 
+			<el-table-column prop="createdAt" label="创建时间" min-width="100" align="center"></el-table-column>
+			<el-table-column label="操作" width="200" align="center">
+				<template #default="scope">
+					<el-button size="small" text type="primary" v-if="!scope.row.folderName"
+						@click="toDetail(scope.row.id)">查看</el-button>
+					<el-button size="small" text type="warning" v-auth="'edit'" @click="addOrEdit(scope.row)">编辑</el-button>
+					<el-button size="small" text type="success" v-auth="'edit'" @click="CheckUpdate(scope.row)">验证</el-button>
+				
+					<el-button size="small" text type="danger" v-auth="'del'" @click="del(scope.row)">删除</el-button>
+				</template>
+			</el-table-column>
+		</el-table>
+		<pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize"
+			@pagination="getList()" />
+
+	</el-card>
+</template>
+  
+<script lang="ts" setup>
+import api from '/@/api/ota';
+import { useSearch } from '/@/hooks/useCommon';
+import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
+import getOrigin from '/@/utils/origin'
+
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+
+const queryRef = ref();
+const router = useRouter();
+
+const editFormRef = ref();
+const checkFormRef = ref();
+
+const { params, tableData, getList, loading } = useSearch<any[]>(api.manage.getList, 'fireware', { keyWord: '' });
+
+getList();
+/** 重置按钮操作 */
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	getList();
+};
+const toDetail = (id: number) => {
+      router.push(`/iotmanager/ota/update/detail/${id}`)
+};
+
+const CheckUpdate= async (row?: any) => {
+	if (row) {
+		checkFormRef.value.open(row);
+		return;
+	} else {
+		checkFormRef.value.open();
+	}
+};
+
+const addOrEdit = async (row?: any) => {
+	if (row) {
+		editFormRef.value.open(row);
+		return;
+	} else {
+		editFormRef.value.open();
+	}
+};
+
+const del = (row: any) => {
+	ElMessageBox.confirm(`此操作将删除图形:“${row.name}”,是否继续?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+	}).then(async () => {
+		await api.manage.del(row.id);
+		ElMessage.success('删除成功');
+		getList();
+	});
+};
+</script>
+  

+ 124 - 0
src/views/iot/ota-update/update/component/device.vue

@@ -0,0 +1,124 @@
+
+<template>
+	<el-card shadow="hover">
+		<div class="search">
+			<el-form :inline="true" ref="queryRef">
+				<el-form-item label="设备名称:" prop="name">
+					<el-input v-model="params.keyWord" placeholder="请输入产品名称" clearable size="default" style="width: 240px"
+						@keyup.enter.native="getList" />
+				</el-form-item>
+		
+				<el-form-item>
+
+					<el-button size="default" type="primary" class="ml10" @click="getList()">
+						<el-icon>
+							<ele-Search />
+						</el-icon>
+						查询
+					</el-button>
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" v-auth="'add'" @click="addOrEdit()">
+						<el-icon>
+							<ele-FolderAdd />
+						</el-icon>
+						添加升级包
+					</el-button>
+
+				</el-form-item>
+			</el-form>
+		</div>
+		<el-table :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
+			<el-table-column prop="id" label="ID" width="60" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="name" label="升级包名称" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="typo" label="类型" show-overflow-tooltip>
+				<template #default="scope">
+					<el-tag  size="small" v-if="scope.row.typo==1">整包</el-tag>
+					<el-tag  type="info" size="small" v-if="scope.row.typo==2">差分</el-tag>
+				</template>
+			</el-table-column>
+			<el-table-column prop="productName" label="所属产品" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="moduleName" label="模块名称" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="name" label="状态" width="100" align="center">
+				<template #default="scope">
+					<el-tag type="success" size="small" v-if="scope.row.status">验证</el-tag>
+					<el-tag type="info" size="small" v-else>未验证</el-tag>
+				</template>
+			</el-table-column> 
+			<el-table-column prop="createdAt" label="创建时间" min-width="100" align="center"></el-table-column>
+			<el-table-column label="操作" width="200" align="center">
+				<template #default="scope">
+					<el-button size="small" text type="primary" v-if="!scope.row.folderName"
+						@click="toDetail(scope.row.id)">查看</el-button>
+					<el-button size="small" text type="warning" v-auth="'edit'" @click="addOrEdit(scope.row)">编辑</el-button>
+					<el-button size="small" text type="success" v-auth="'edit'" @click="CheckUpdate(scope.row)">验证</el-button>
+				
+					<el-button size="small" text type="danger" v-auth="'del'" @click="del(scope.row)">删除</el-button>
+				</template>
+			</el-table-column>
+		</el-table>
+		<pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize"
+			@pagination="getList()" />
+
+	</el-card>
+</template>
+  
+<script lang="ts" setup>
+import api from '/@/api/ota';
+import { useSearch } from '/@/hooks/useCommon';
+import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
+import getOrigin from '/@/utils/origin'
+
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+
+const queryRef = ref();
+const router = useRouter();
+
+const editFormRef = ref();
+const checkFormRef = ref();
+
+const { params, tableData, getList, loading } = useSearch<any[]>(api.manage.getList, 'fireware', { keyWord: '' });
+
+getList();
+/** 重置按钮操作 */
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	getList();
+};
+const toDetail = (id: number) => {
+      router.push(`/iotmanager/ota/update/detail/${id}`)
+};
+
+const CheckUpdate= async (row?: any) => {
+	if (row) {
+		checkFormRef.value.open(row);
+		return;
+	} else {
+		checkFormRef.value.open();
+	}
+};
+
+const addOrEdit = async (row?: any) => {
+	if (row) {
+		editFormRef.value.open(row);
+		return;
+	} else {
+		editFormRef.value.open();
+	}
+};
+
+const del = (row: any) => {
+	ElMessageBox.confirm(`此操作将删除图形:“${row.name}”,是否继续?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+	}).then(async () => {
+		await api.manage.del(row.id);
+		ElMessage.success('删除成功');
+		getList();
+	});
+};
+</script>
+  

+ 173 - 0
src/views/iot/ota-update/update/component/info.vue

@@ -0,0 +1,173 @@
+<template>
+	<el-card class="system-dic-container" style="position: relative">
+		<div class="content">
+		
+			<div class="container">
+				<div class="item">升级包 ID:BV2IA9D4wF1b3ZfcRfWQ030100</div>
+				<div class="item">升级包名称:测试</div>
+				<div class="item">所属产品:测试</div>
+			</div>
+			<div class="container">
+				<div class="item">升级包签名:d52b637c5eaf2bc9c24008bc4b723600</div>
+				<div class="item">升级包版本号:1.0</div>
+				<div class="item">创建时间:2023/09/30 13:08:46</div>
+			</div>
+			<div class="container">
+				<div class="item">签名算法:MD5</div>
+				<div class="item">升级包状态:未验证</div>
+				<div class="item">验证进度:0%</div>
+			</div>
+			<div class="container">
+				<div class="item">升级包描述:22222222222222</div>
+				<div class="item">推送给设备的自定义信息:22222222222222233</div>
+				<div class="item"></div>
+			</div>
+			
+		</div>
+	</el-card>
+</template>
+<script lang="ts">
+import { toRefs, reactive, onMounted, defineComponent } from 'vue'
+import type { TabsPaneContext } from 'element-plus'
+import { useRoute } from 'vue-router'
+import { EditPen, DocumentAdd } from '@element-plus/icons-vue'
+import api from '/@/api/network'
+
+
+export default defineComponent({
+	components: { EditPen, DocumentAdd },
+	setup(props, context) {
+		const route = useRoute()
+		const state = reactive({
+			developer_status: 2,
+			detail: {},
+			
+		})
+		const getDetail = () => {
+			const id = route.params && route.params.id
+			api.server.getDetail({ id: id }).then((res: any) => {
+				state.detail = res
+			})
+		}
+	
+	
+	
+		onMounted(() => {
+			 getDetail()
+		})
+		const handleClick = (tab: TabsPaneContext, event: Event) => {
+			// console.log(tab, event)
+		}
+
+		return {
+			getDetail,
+			handleClick,
+			...toRefs(props),
+			...toRefs(state),
+		}
+	},
+})
+</script>
+
+<style scoped lang="scss">
+.status_list{
+	width: 100%;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+	padding: 10px;
+	.otaflex{
+		font-size: 14px;
+		display: flex;
+		align-items: center;
+		margin-left: -6px;
+		.otaflex_div1{
+			padding: 0 24px;
+			min-width: 200px;
+			width: fit-content;
+			.otaflex_div2 {
+				align-items: center;
+				.title{
+					color: #666;
+					font-size: 14px;
+				}
+				span {
+					display: block;
+					border-radius: 50%;
+					.on {
+						background: #52c41a;
+					}
+					.off {
+						background: #c41a1a;
+					}
+					.ofn {
+						background: rgb(255, 138, 0);
+					}
+
+					span {
+						position: relative;
+						top: -1px;
+						display: inline-block;
+						width:10px;
+						height: 10px;
+						vertical-align: middle;
+						border-radius: 50%;
+						margin-right: 5px;
+					}
+
+				}
+				.otaflex_div3{
+					font-size: 24px;
+					margin-top: 10px;
+					color: #373d41;
+				}
+			}
+	
+	
+		}
+		
+	}
+}
+.container {
+	display: flex;
+	padding: 10px;
+}
+
+.item {
+	flex: 1;
+}
+.desc {
+	margin-top: 15px;
+}
+
+.edit {
+	line-height: 40px;
+	margin-top: 15px;
+	margin-left: 30px;
+}
+
+.cont_box .pro-status {
+	line-height: 40px;
+	margin-left: 30px;
+	margin-top: 5px;
+
+	.on {
+		background: #52c41a;
+	}
+
+	.off {
+		background: #c41a1a;
+	}
+
+	span {
+		position: relative;
+		top: -1px;
+		display: inline-block;
+		width: 6px;
+		height: 6px;
+		vertical-align: middle;
+		border-radius: 50%;
+		margin-right: 5px;
+	}
+}
+</style>

+ 231 - 0
src/views/iot/ota-update/update/detail.vue

@@ -0,0 +1,231 @@
+<template>
+	<el-card class="system-dic-container" style="position: relative">
+		<div class="content">
+			<div class="flex cont_box">
+				<div class="font26">升级包名称:店里车间场景</div>
+				<div class="pro-status"><span :class="developer_status == 2 ? 'on' : 'off'"></span>{{ developer_status == 2 ? '已验证' : '未验证' }}</div>
+			</div>
+			<div class="mt20"></div>
+			<div class="container">
+				<div class="item">升级包类型:整包</div>
+				<div class="item">升级包签名:d52b637c5eaf2bc9c24008bc4b723600</div>
+			</div>
+			<div class="container">
+				<div class="item">签名算法:MD5</div>
+				<div class="item">模块名称:default</div>
+			</div>
+			<div class="mt20"></div>
+			<div class="status_list">
+				<div class="otaflex">
+					<div class="otaflex_div1">
+							<div class="otaflex_div2">
+								<span class="title">目标设备总数</span>
+								<div class="count otaflex_div3">0</div>
+							</div>
+						</div>
+						<div class="otaflex_div1">
+							<div class="otaflex_div2">
+								<span class="title"><span class="on"></span>目标成功数</span>
+								<div class="count otaflex_div3">0</div>
+							</div>
+
+						</div>
+						<div class="otaflex_div1">
+							<div class="otaflex_div2">
+								<span class="title"><span class="off"></span>目标失败数</span>
+								<div class="count otaflex_div3">0</div>
+							</div>
+						</div>
+						<div class="otaflex_div1">
+							<div class="otaflex_div2">
+								<span class="title"><span class="ofn"></span>目标取消数</span>
+								<div class="count otaflex_div3">0</div>
+							</div>
+
+						</div>
+				</div>
+			</div>
+		</div>
+	</el-card>
+	<div class="mt10"></div>
+	<el-card class="system-dic-container" style="position: relative">
+		<el-tabs v-model="activeTab">
+				<el-tab-pane label="批次管理" name="tab1">
+					<BatchList :updata_id="detail.id"></BatchList>
+				</el-tab-pane>
+				<el-tab-pane label="设备列表" name="tab2">
+					<DeviceList :updata_id="detail.id"></DeviceList>
+				</el-tab-pane>
+				<el-tab-pane label="升级包信息" name="tab3">
+					<InfoList :updata_id="detail.id"></InfoList>
+				</el-tab-pane>
+			</el-tabs>
+	</el-card>
+
+</template>
+<script lang="ts">
+import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue'
+import { ElMessage } from 'element-plus'
+import type { TabsPaneContext } from 'element-plus'
+import { useRoute, useRouter } from 'vue-router'
+import { EditPen, DocumentAdd } from '@element-plus/icons-vue'
+import EditForm from './edit.vue'
+import InfoList from './component/info.vue'
+import DeviceList from './component/device.vue'
+import BatchList from './component/batch.vue'
+import api from '/@/api/network'
+
+const editFormRef = ref()
+
+export default defineComponent({
+	components: { EditPen, EditForm, DocumentAdd,InfoList,DeviceList,BatchList},
+	setup(props, context) {
+		const route = useRoute()
+		const router = useRouter()
+		const state = reactive({
+			activeTab:'tab1',
+			developer_status: 2,
+			detail: {},
+			
+		})
+		const getDetail = () => {
+			const id = route.params && route.params.id
+			api.server.getDetail({ id: id }).then((res: any) => {
+				state.detail = res
+			})
+		}
+		const addOrEdit = async (row?: any) => {
+			editFormRef.value.open(row)
+		}
+
+		const freshData = () => {
+			getDetail()
+			ElMessage.success('刷新成功')
+		}
+		const toEdit = () => {
+			router.push(`/iotmanager/network/server/edit/${route.params && route.params.id}`)
+		}
+		onMounted(() => {
+			// getDetail()
+		})
+		const handleClick = (tab: TabsPaneContext, event: Event) => {
+			// console.log(tab, event)
+		}
+
+		return {
+			toEdit,
+			addOrEdit,
+			editFormRef,
+			freshData,
+			getDetail,
+			handleClick,
+			...toRefs(props),
+			...toRefs(state),
+		}
+	},
+})
+</script>
+
+<style scoped lang="scss">
+.status_list{
+	width: 100%;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+	padding: 10px;
+	.otaflex{
+		font-size: 12px;
+		display: flex;
+		align-items: center;
+		margin-left: -6px;
+		.otaflex_div1{
+			padding: 0 24px;
+			min-width: 200px;
+			width: fit-content;
+			.otaflex_div2 {
+				align-items: center;
+				.title{
+					color: #666;
+					font-size: 14px;
+				}
+				span {
+					display: block;
+					border-radius: 50%;
+					.on {
+						background: #52c41a;
+					}
+
+					.off {
+						background: #c41a1a;
+					}
+					.ofn {
+						background: rgb(255, 138, 0);
+					}
+
+					span {
+						position: relative;
+						top: -1px;
+						display: inline-block;
+						width:10px;
+						height: 10px;
+						vertical-align: middle;
+						border-radius: 50%;
+						margin-right: 5px;
+					}
+
+				}
+				.otaflex_div3{
+					font-size: 24px;
+					margin-top: 10px;
+					color: #373d41;
+				}
+			}
+	
+	
+		}
+		
+	}
+}
+.container {
+	display: flex;
+	padding: 10px;
+}
+
+.item {
+	flex: 1;
+}
+.desc {
+	margin-top: 15px;
+}
+
+.edit {
+	line-height: 40px;
+	margin-top: 15px;
+	margin-left: 30px;
+}
+
+.cont_box .pro-status {
+	line-height: 40px;
+	margin-left: 30px;
+	margin-top: 5px;
+
+	.on {
+		background: #52c41a;
+	}
+
+	.off {
+		background: #c41a1a;
+	}
+
+	span {
+		position: relative;
+		top: -1px;
+		display: inline-block;
+		width: 6px;
+		height: 6px;
+		vertical-align: middle;
+		border-radius: 50%;
+		margin-right: 5px;
+	}
+}
+</style>

+ 216 - 0
src/views/iot/ota-update/update/edit.vue

@@ -0,0 +1,216 @@
+<template>
+	<el-dialog
+		class="api-edit"
+		v-model="showDialog"
+		:title="`${formData.id ? '编辑升级包' : '新增升级包'}`"
+		width="768px"
+		:close-on-click-modal="false"
+		:close-on-press-escape="false"
+	>
+		<el-form ref="formRef" :model="formData" :rules="ruleForm" label-width="160px">
+			<el-form-item label="升级包类型" prop="typo">
+				<el-radio-group v-model="formData.typo">
+					<el-radio label="1">整包</el-radio>
+					<el-radio label="2">差分</el-radio>
+				</el-radio-group>
+			</el-form-item>
+
+			<el-form-item label="升级包名称" prop="name">
+				<el-input v-model="formData.name" placeholder="请输入升级包名称" />
+			</el-form-item>
+			<el-form-item label="所属产品" prop="productId">
+				<el-select v-model="formData.productId" filterable placeholder="请选择产品" @change="seletChange">
+					<el-option v-for="item in productData" :key="item.id" :label="item.name" :value="item.id.toString()" value-key="id"> </el-option>
+				</el-select>
+			</el-form-item>
+			<div>
+				<el-form-item label="升级包模块" prop="module">
+					<el-select v-model="formData.module" filterable placeholder="请选择产品">
+						<el-option v-for="item in moduleData" :key="item.id" :label="item.name" :value="item.id.toString()" value-key="id"> </el-option>
+				</el-select>
+				</el-form-item>
+			</div>
+			<el-form-item label="升级包版本号" prop="version" v-if="formData.typo == 1">
+				<el-input v-model="formData.version" placeholder="请输入待升级包版本号" />
+			</el-form-item>
+
+			<el-form-item label="待升级版本号" prop="waitVersion" v-if="formData.typo == 2">
+				<el-input v-model="formData.waitVersion" placeholder="请输入待升级版本号" />
+			</el-form-item>
+
+			<el-form-item label="升级后版本号" prop="afterVersion" v-if="formData.typo == 2">
+				<el-input v-model="formData.afterVersion" placeholder="请输入升级后版本号" />
+			</el-form-item>
+
+			<el-form-item label="算法签名" prop="are">
+				<el-select v-model="formData.are" filterable placeholder="请选择算法签名">
+					<el-option v-for="item in areType" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+				</el-select>
+			</el-form-item>
+
+			<el-form-item label="选择升级包" prop="url">
+				<el-upload :accept="['.bin','.tar','.gz','.tar.xz','.zip','.gzip','.apk','.dav','.pack']" :show-file-list="false" :limit="1" :headers="headers" :action="uploadUrl" :on-success="updateImg">
+				
+					<el-button type="Default">上传升级包</el-button>
+				</el-upload>
+			</el-form-item>
+
+			<el-form-item label="升级包是否验证" prop="check">
+				<el-radio-group v-model="formData.check">
+					<el-radio :label="1">是</el-radio>
+					<el-radio :label="2">否</el-radio>
+				</el-radio-group>
+			</el-form-item>
+			<el-form-item label="升级包描述" prop="describe">
+				<el-input v-model="formData.describe" type="textarea" :rows="3" />
+			</el-form-item>
+
+			<el-form-item label="推送设备的自定义消息" prop="info">
+				<el-input v-model="formData.info" type="textarea" :rows="3" />
+			</el-form-item>
+		</el-form>
+		<template #footer>
+			<div class="dialog-footer">
+				<el-button @click="showDialog = false">取消</el-button>
+				<el-button type="primary" @click="onSubmit">确定</el-button>
+			</div>
+		</template>
+	</el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, nextTick } from 'vue'
+import api from '/@/api/ota'
+import { ruleRequired } from '/@/utils/validator'
+import { ElMessage } from 'element-plus'
+import getOrigin from '/@/utils/origin';
+
+
+const emit = defineEmits(['getList'])
+
+const showDialog = ref(false)
+const formRef = ref()
+const uploadUrl: string = getOrigin(import.meta.env.VITE_API_URL + "/dev_ota_fireware/upload");
+		const headers = {
+			Authorization: 'Bearer ' + localStorage.token,
+		};
+const areType = ref([
+	{
+		label: 'MD5',
+		value: 'MD5',
+	},
+	{
+		label: 'SHA256',
+		value: 'SHA256',
+	},
+])
+const productData = ref([
+	{
+		id: '',
+		name: '',
+	},
+])
+const moduleData = ref([
+	{
+		id: '',
+		name: '',
+	},
+])
+const baseForm = {
+	id: undefined,
+	typo: '1',
+	name: '',
+	productId: '',
+	module: '',
+	version: '',
+	are: 'MD5',
+	url: '',
+	check: 1,
+	describe: '',
+	info: '',
+	afterVersion: '',
+	waitVersion: '',
+}
+
+const formData = reactive({
+	...baseForm,
+})
+
+const ruleForm = {
+	typo: [ruleRequired('请选择升级包类型')],
+	name: [ruleRequired('升级包名称不能为空')],
+	productId: [ruleRequired('请选择所属产品')],
+	module: [ruleRequired('请选择升级包模块')],
+	version: [ruleRequired('升级包版本号不能为空')],
+	are: [ruleRequired('算法签名不能为空')],
+	check: [ruleRequired('升级包是否验证不能为空')],
+	// url: [ruleRequired('升级包不能为空')],
+	afterVersion: [ruleRequired('待升级版本号不能为空')],
+	waitVersion: [ruleRequired('升级后版本号不能为空')],
+}
+
+const updateImg = (res: any) => {
+	console.log(res);
+
+			if (res.code === 0) {
+				ElMessage.success('上传成功');
+			} else {
+				ElMessage.error(res.message);
+			}
+		};
+
+const onSubmit = async () => {
+	await formRef.value.validate()
+
+	const theApi = formData.id ? api.manage.edit : api.manage.add
+
+	await theApi(formData)
+
+	ElMessage.success('操作成功')
+	resetForm()
+	showDialog.value = false
+	emit('getList')
+}
+
+const seletChange = (val: Number) => {
+	formData.module='';
+	getModuleList(val);
+};
+
+const getModuleList = (productID: Number) => {
+	api.module.getList({productID:productID}).then((res: any) => {
+		let productDataList = res.Data
+		moduleData.value = productDataList
+	})
+}
+
+const getProductList = () => {
+	api.module.getSubList().then((res: any) => {
+		let productDataList = res.product
+		productData.value = productDataList
+	})
+}
+getProductList()
+
+const resetForm = async () => {
+	Object.assign(formData, { ...baseForm })
+	formRef.value && formRef.value.resetFields()
+}
+
+const open = async (row: any) => {
+	resetForm()
+	showDialog.value = true
+	nextTick(() => {
+		Object.assign(formData, { ...row })
+	})
+}
+
+defineExpose({ open })
+</script>
+<style>
+.vertical-form-item {
+	display: block;
+	margin-bottom: 10px;
+	/* 可选,设置间距 */
+}
+</style>

+ 126 - 0
src/views/iot/ota-update/update/index.vue

@@ -0,0 +1,126 @@
+
+<template>
+	<el-card shadow="hover">
+		<div class="search">
+			<el-form :inline="true" ref="queryRef">
+				<el-form-item label="升级包名称:" prop="name">
+					<el-input v-model="params.keyWord" placeholder="请输入产品名称" clearable size="default" style="width: 240px"
+						@keyup.enter.native="getList" />
+				</el-form-item>
+
+				<el-form-item>
+
+					<el-button size="default" type="primary" class="ml10" @click="getList()">
+						<el-icon>
+							<ele-Search />
+						</el-icon>
+						查询
+					</el-button>
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" v-auth="'add'" @click="addOrEdit()">
+						<el-icon>
+							<ele-FolderAdd />
+						</el-icon>
+						添加升级包
+					</el-button>
+
+				</el-form-item>
+			</el-form>
+		</div>
+		<el-table :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
+			<el-table-column prop="id" label="ID" width="60" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="name" label="升级包名称" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="typo" label="类型" show-overflow-tooltip>
+				<template #default="scope">
+					<el-tag  size="small" v-if="scope.row.typo==1">整包</el-tag>
+					<el-tag  type="info" size="small" v-if="scope.row.typo==2">差分</el-tag>
+				</template>
+			</el-table-column>
+			<el-table-column prop="productName" label="所属产品" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="moduleName" label="模块名称" show-overflow-tooltip></el-table-column>
+			<el-table-column prop="name" label="状态" width="100" align="center">
+				<template #default="scope">
+					<el-tag type="success" size="small" v-if="scope.row.status">验证</el-tag>
+					<el-tag type="info" size="small" v-else>未验证</el-tag>
+				</template>
+			</el-table-column> 
+			<el-table-column prop="createdAt" label="创建时间" min-width="100" align="center"></el-table-column>
+			<el-table-column label="操作" width="200" align="center">
+				<template #default="scope">
+					<el-button size="small" text type="primary" v-if="!scope.row.folderName"
+						@click="toDetail(scope.row.id)">查看</el-button>
+					<el-button size="small" text type="warning" v-auth="'edit'" @click="addOrEdit(scope.row)">编辑</el-button>
+					<el-button size="small" text type="success" v-auth="'edit'" @click="CheckUpdate(scope.row)">验证</el-button>
+				
+					<el-button size="small" text type="danger" v-auth="'del'" @click="del(scope.row)">删除</el-button>
+				</template>
+			</el-table-column>
+		</el-table>
+		<pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize"
+			@pagination="getList()" />
+		<EditForm ref="editFormRef" @getList="getList()"></EditForm>
+		<CheckForm ref="checkFormRef" @getList="getList()"></CheckForm>
+	</el-card>
+</template>
+  
+<script lang="ts" setup>
+import api from '/@/api/ota';
+import { useSearch } from '/@/hooks/useCommon';
+import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
+import getOrigin from '/@/utils/origin'
+import EditForm from './edit.vue';
+import CheckForm from './check.vue';
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+
+const queryRef = ref();
+const router = useRouter();
+
+const editFormRef = ref();
+const checkFormRef = ref();
+
+const { params, tableData, getList, loading } = useSearch<any[]>(api.manage.getList, 'fireware', { keyWord: '' });
+
+getList();
+/** 重置按钮操作 */
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	getList();
+};
+const toDetail = (id: number) => {
+      router.push(`/iotmanager/ota/update/detail/${id}`)
+};
+
+const CheckUpdate= async (row?: any) => {
+	if (row) {
+		checkFormRef.value.open(row);
+		return;
+	} else {
+		checkFormRef.value.open();
+	}
+};
+
+const addOrEdit = async (row?: any) => {
+	if (row) {
+		editFormRef.value.open(row);
+		return;
+	} else {
+		editFormRef.value.open();
+	}
+};
+
+const del = (row: any) => {
+	ElMessageBox.confirm(`此操作将删除图形:“${row.name}”,是否继续?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+	}).then(async () => {
+		await api.manage.del(row.id);
+		ElMessage.success('删除成功');
+		getList();
+	});
+};
+</script>
+