Преглед изворни кода

feat: 添加实验流程组件及相关功能

yusheng пре 3 месеци
родитељ
комит
7aa2ae052f

+ 2 - 2
App.vue

@@ -18,7 +18,7 @@
 			console.log('Vue version:~~~', Vue.version);
 			// #ifdef APP-PLUS
 			const apiInfo = uni.getStorageSync('apiInfo')
-			console.log('apiInfo:', apiInfo)
+			// console.log('apiInfo:', apiInfo)
 			if (apiInfo.protocal) {
 				Vue.prototype.apiUrl = `${apiInfo.protocal}${apiInfo.hostname}:${apiInfo.port}/api`
 				Vue.prototype.webviewUrl = `${apiInfo.protocal}${apiInfo.hostname}:${apiInfo.port}`
@@ -36,7 +36,7 @@
 			}
 
 			getLatestVersion().then(res => {
-				console.log('res:', res)
+				// console.log('res:', res)
 				const {
 					appVersion
 				} = uni.getAppBaseInfo()

+ 343 - 0
components/templateDiv/customTable.vue

@@ -0,0 +1,343 @@
+<!-- 移动端表格组件 -->
+<template>
+	<view class="custom-table-container">
+		<!-- 表格容器 -->
+		<div class="table-wrapper" >
+			<view class="table" :id="id">
+				<view class="table-body">
+					<template v-for="(row, index) in columns">
+						<view class="column" :style="{ width: (row[0] && row[0].width) ? row[0].width + 'rpx' : '200rpx' }">
+							<view class="table-body-item" v-for="(item, rowIndex) in row" :key="item.id" :style="{
+                  height: item.rowspan > 1 ? (item.rowspan * 60) + 'rpx' : '60rpx',
+                  display: item.rowspan ? 'flex' : 'none',
+                  ...item.style,
+                  width: item.colspan ? getWidth(item) : 'auto'
+                }" :class="{
+                  'is-header': rowIndex === 0,
+                 
+                  'is-readonly': equation[item.id]
+                }">
+
+								<!-- 输入框 -->
+								<view class="input-wrapper">
+									
+									<textarea v-if="item.rowspan > 1" v-model="item.value" class="template-input"
+										:id="item.id" :ref="item.id + 'ref'" :readonly="item.readonly == 2 || readonly||equation[item.id]"
+										@input="calculation" :auto-height="true" :maxlength="200" />
+									<input v-else v-model="item.value" class="template-input" :id="item.id"
+										:ref="item.id + 'ref'" :disabled="item.readonly == 2 || readonly||equation[item.id]"
+										@input="calculation" type="text" />
+								</view>
+							</view>
+						</view>
+					</template>
+				</view>
+			</view>
+		</div>
+
+
+	</view>
+</template>
+
+<script>
+	import dictMixins from '@/mixins/dictMixins';
+
+
+	export default {
+		mixins: [dictMixins],
+		props: {
+			id: {
+				type: String,
+				default: ''
+			},
+
+			readonly: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				form: null,
+				valueObj: {},
+				domId: '',
+				columns: [],
+				units: {},
+				equation: {},
+			
+			};
+		},
+		computed: {
+	
+		},
+		methods: {
+			getWidth(item) {
+				let width = Number(item.style.width);
+				if (item.colspanKey.length) {
+					this.columns.forEach(cell => {
+						cell.forEach(row => {
+							if (item.colspanKey.includes(row.id)) {
+								width += Number(row.style.width);
+							}
+						});
+					});
+				}
+				return width + 'rpx';
+			},
+
+			calculation() {
+				this.$emit('calculation');
+			},
+
+			equationValue({
+				domId,
+				value
+			}) {
+				this.columns.forEach((item, index) => {
+					let cellIndex = item.findIndex(cell => cell.id == domId);
+					if (cellIndex != '-1') {
+						this.$set(this.columns[index][cellIndex], 'value', value);
+					}
+				});
+			},
+
+			getValue() {
+				return {
+					form: null,
+					equation: this.equation,
+					units: this.units,
+					valueObj: {
+						columns: this.columns
+					}
+				};
+			},
+
+			init({
+				form,
+				valueObj,
+				equation,
+				units
+			}) {
+				this.form = form;
+				this.columns = valueObj.columns;
+				this.equation = equation || {};
+				this.units = units || {};
+				console.log(this.units);
+				console.log(this.equation);
+			},
+
+			editInputChange(domObj) {
+				let data = JSON.parse(JSON.stringify(domObj));
+				if (data.equation) {
+					this.equation[data.id] = data.equation;
+				}
+				if (data.units) {
+					this.units[data.id] = data.units;
+				}
+				// 移动端简化处理
+				this.columns.forEach((item, index) => {
+					let rowsIndex = item.findIndex(cells => cells.id == this.domId);
+					this.$set(this.columns[index], rowsIndex, data);
+					rowsIndex = item.findIndex(cells => cells.id == data.id);
+					if (rowsIndex >= 0) {
+						let width = data.width;
+						let newWidth = this.columns[index][rowsIndex].style.width;
+						this.columns[index][0].width = newWidth;
+						item.forEach((cell, _index) => {
+							this.$set(this.columns[index][_index].style, 'width', newWidth);
+							this.$set(this.columns[index][_index], 'width', newWidth);
+						});
+					}
+				});
+			},
+
+
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.custom-table-container {
+		width: 100%;
+		// overflow: hidden;
+
+
+
+		.table-wrapper {
+			width: 100%;
+			// margin-top: 20rpx;
+
+			.table {
+				display: flex;
+				min-width: 100%;
+
+				.table-body {
+					display: flex;
+
+					.column {
+						display: inline-block;
+
+						.table-body-item {
+							position: relative;
+							display: flex;
+							align-items: center;
+							justify-content: center;
+							border: 1rpx solid #ddd;
+							background-color: #fff;
+							overflow: hidden;
+
+							&.is-header {
+								// background-color: #f2f2f2;
+
+								.template-input {
+									// background-color: #f2f2f2;
+								}
+							}
+
+							&.is-selected {
+								// background-color: #e6f7ff;
+								border-color: #1890ff;
+							}
+
+							&.is-readonly {
+								background-color: #f9f9f9;
+							}
+
+							.cell-actions {
+								position: absolute;
+								top: 0;
+								right: 0;
+								display: none;
+								z-index: 10;
+
+								.action-btn {
+									width: 40rpx;
+									height: 40rpx;
+									line-height: 40rpx;
+									text-align: center;
+									background-color: rgba(0, 0, 0, 0.5);
+									color: #fff;
+									border-radius: 50%;
+									font-size: 28rpx;
+
+									&.delete {
+										color: #f56c6c;
+									}
+
+									&.add {
+										color: #409eff;
+										margin-left: 5rpx;
+									}
+
+									&.deleteRow {
+										color: #f56c6c;
+									}
+
+									&.addRow {
+										color: #409eff;
+										margin-left: 5rpx;
+									}
+								}
+							}
+
+							&:active .cell-actions {
+								display: flex;
+							}
+
+							.input-wrapper {
+								width: 100%;
+								height: 100%;
+								display: flex;
+								align-items: center;
+								justify-content: center;
+							}
+
+							.template-input {
+								width: 100%;
+								height: 100%;
+								border: none;
+								text-align: center;
+								background-color: transparent;
+								font-size: 24rpx;
+								padding: 0;
+								line-height: 1.5;
+							}
+
+							textarea.template-input {
+								padding: 10rpx;
+								resize: none;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		.merge-tip {
+			position: fixed;
+			top: 50%;
+			left: 50%;
+			transform: translate(-50%, -50%);
+			padding: 20rpx 40rpx;
+			background-color: rgba(0, 0, 0, 0.7);
+			color: #fff;
+			border-radius: 8rpx;
+			font-size: 28rpx;
+			z-index: 1000;
+		}
+
+		.bottom-actions {
+			position: fixed;
+			bottom: 0;
+			left: 0;
+			right: 0;
+			z-index: 999;
+
+			.action-sheet {
+				background-color: #fff;
+				border-top: 1rpx solid #e0e0e0;
+
+				.sheet-header {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					padding: 30rpx;
+					border-bottom: 1rpx solid #e0e0e0;
+
+					.title {
+						font-size: 32rpx;
+						font-weight: bold;
+						color: #333;
+					}
+
+					.cancel {
+						font-size: 28rpx;
+						color: #999;
+					}
+				}
+
+				.sheet-content {
+					padding: 30rpx;
+
+					.sheet-btn {
+						width: 100%;
+						height: 80rpx;
+						line-height: 80rpx;
+						text-align: center;
+						border-radius: 8rpx;
+						font-size: 32rpx;
+
+						&.primary {
+							background-color: #157a2c;
+							color: #fff;
+
+							&:active {
+								background-color: #0f5e20;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 260 - 0
components/templateDiv/customText.vue

@@ -0,0 +1,260 @@
+<!-- 移动端自定义文本编辑组件 - 使用 renderjs 支持 contenteditable -->
+<template>
+	<view class="custom-text-container">
+		<view id="htmlContent" class="content-editable" :contenteditable="!readonly" v-html="form" :prop="renderProps"
+			:change:prop="renderScript.handlePropChange">
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			id: {
+				type: String,
+				default: "",
+			},
+			readonly: {
+				default: false,
+				type: Boolean,
+			},
+		},
+		data() {
+			return {
+				form: null,
+				valueObj: {},
+				equation: {},
+				units: {},
+				domId: "",
+				cachedHtml: "",
+				equationData: null,
+			};
+		},
+		computed: {
+			// 合并所有需要传递给 renderjs 的数据
+			renderProps() {
+				return {
+					form: this.form,
+					valueObj: this.valueObj,
+					equationData: this.equationData,
+					equation: this.equation,
+					readonly: this.readonly,
+					timestamp: Date.now(), // 确保每次都有变化
+				};
+			},
+		},
+		methods: {
+			// renderjs 回调:失去焦点
+			onHtmlBlur({
+				html
+			}) {
+				this.cachedHtml = html;
+			},
+			onInputINPUT(data) {
+				console.log("input 值变化:", data);
+				if (data.inputId) {
+					if (data.inputType === "checkbox") {
+						this.valueObj[data.inputId] = data.checked;
+					} else {
+						this.valueObj[data.inputId] = data.value;
+					}
+				}
+				this.$emit("calculation");
+			},
+
+			equationValue({
+				domId,
+				value
+			}) {
+				console.log(value, "value");
+				this.valueObj[domId] = value;
+				this.equationData = {
+					domId: domId,
+					value: value,
+					timestamp: Date.now(), // 确保每次都能触发 change
+				};
+
+				console.log(this.equationData, "this.equationData");
+			},
+			// 获取值
+			getValue() {
+				return {
+					form: this.cachedHtml || this.form || "",
+					valueObj: this.valueObj,
+					equation: this.equation,
+					units: this.units,
+				};
+			},
+
+			// 初始化
+			init({
+				form,
+				valueObj,
+				equation,
+				units
+			}) {
+				this.form = form || "";
+				this.valueObj = valueObj || {};
+				console.log(this.valueObj, "this.valueObj");
+				this.equation = equation || {};
+				this.units = units || {};
+				this.cachedHtml = this.form;
+			},
+		},
+	};
+</script>
+
+<script module="renderScript" lang="renderjs">
+	export default {
+		data() {
+			return {
+
+			};
+		},
+		mounted() {
+			console.log('renderjs mounted');
+			this.initEditor();
+		},
+		methods: {
+			// 统一处理所有 prop 变化
+			handlePropChange(newVal, oldVal) {
+				if (!newVal) return;
+
+				console.log('renderjs 收到数据:', newVal);
+
+				// 处理初始化数据 (valueObj)
+				if (newVal.valueObj && Object.keys(newVal.valueObj).length > 0) {
+					this.initValues(newVal.valueObj);
+				}
+
+				// 处理公式计算数据 (equationData)
+				if (newVal.equationData && newVal.equationData.domId) {
+					this.updateEquationValue(newVal.equationData);
+				}
+				if (newVal.readonly) {
+					if (this.readonly) {
+						let inputs = document.querySelectorAll('.templateInput');
+						inputs.forEach((item) => {
+							item.setAttribute('readonly', 'readonly');
+						});
+					}
+				}
+				if (newVal.equation.length) {
+					for (key in newVal.equation) {
+						const dom = document.getElementById(key);
+						dom.setAttribute('readonly', 'readonly');
+					}
+
+				}
+			},
+
+			// 初始化 input 值
+			initValues(valueObj) {
+				console.log('初始化值:', valueObj);
+				for (let key in valueObj) {
+					const dom = document.getElementById(key);
+					if (dom && dom.tagName === 'INPUT') {
+						if (dom.type === 'checkbox') {
+							dom.checked = valueObj[key];
+						} else {
+							dom.value = valueObj[key];
+						}
+					}
+				}
+			},
+
+			// 更新公式计算值
+			updateEquationValue(equationData) {
+				console.log('更新公式值:', equationData);
+				const {
+					domId,
+					value
+				} = equationData;
+
+				const input = document.getElementById(domId);
+				if (input && input.tagName === 'INPUT') {
+					input.value = value;
+					// // 触发 input 事件通知逻辑层
+					// const event = new Event('input', { bubbles: true });
+					// input.dispatchEvent(event);
+				}
+			},
+			// 初始化编辑器
+			initEditor() {
+				const el = document.getElementById('htmlContent');
+				if (!el) {
+					console.log('htmlContent 元素不存在');
+					return;
+				}
+
+				// el.style.border = '1px solid #ddd';
+				el.style.padding = '10px';
+				el.style.minHeight = '100px';
+				el.style.overflow = 'auto';
+
+				// 监听 input 事件
+				el.addEventListener('input', this.handleInput.bind(this));
+
+				console.log('renderjs 编辑器初始化完成');
+			},
+
+
+
+			// 处理 input 事件
+			handleInput(e) {
+				const html = e.target.innerHTML;
+				// 通知逻辑层 HTML 内容变化
+				if (e.target.tagName === 'INPUT') {
+					const data = {
+						inputId: e.target.id,
+						value: e.target.value,
+						checked: e.target.checked,
+						inputType: e.target.type
+					};
+					this.$ownerInstance.callMethod('onInputINPUT', data);
+
+				} else {
+					this.$ownerInstance.callMethod('onHtmlBlur', {
+						html
+					});
+				}
+			},
+
+
+
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.custom-text-container {
+		width: 100%;
+		min-height: 200rpx;
+		position: relative;
+		background-color: #fff;
+	}
+
+	.content-editable {
+		width: 100%;
+		min-height: 150rpx;
+		padding: 10px;
+		background-color: #fff;
+		border-radius: 4px;
+		font-size: 14px;
+		line-height: 1.5;
+		word-wrap: break-word;
+	}
+
+	/* contenteditable 内部样式 */
+	.content-editable :deep(input) {
+		border: 1px solid #ddd;
+		padding: 2px 5px;
+		border-radius: 3px;
+		font-size: 14px;
+	}
+
+	.content-editable :deep(input:focus) {
+		outline: none;
+		border-color: #007aff;
+	}
+</style>

+ 576 - 0
components/templateDiv/experimentationProcess.vue

@@ -0,0 +1,576 @@
+<!-- 移动端实验流程组件 -->
+<template>
+	<view class="experiment-process-container">
+
+
+		<view class="list-item" v-for="(item, index) in list" :key="item.id" :data-index="index">
+
+
+			<!-- 组件内容 -->
+			<view class="item-content">
+				<customText :ref="'customTextRef' + item.id" v-if="item.type === 'customText'" :id="item.id"
+					@calculation="calculation" :readonly="readonly">
+				</customText>
+				<customTable :ref="'customTextRef' + item.id" v-if="item.type == 'customTable'" :id="item.id"
+					@calculation="calculation" :readonly="readonly">
+				</customTable>
+			</view>
+		</view>
+
+
+
+	</view>
+</template>
+
+<script>
+	import customText from './customText.vue';
+	import customTable from './customTable.vue';
+
+	export default {
+		components: {
+			customText,
+			customTable
+		},
+		props: {
+
+			readonly: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				list: [],
+
+
+
+
+			};
+		},
+		methods: {
+			// 计算
+			calculation() {
+				this.getValue();
+				let equation = [];
+
+				this.list.forEach(item => {
+					equation.push({
+						id: item.id,
+						equation: item.equation
+					});
+				});
+
+				equation.forEach(item => {
+					for (const key in item.equation) {
+						let {
+							data,
+							units
+						} = this.getObjValue();
+						let value = '';
+						if (item.equation[key].length) {
+							item.equation[key].forEach(equationItem => {
+								if (equationItem.type == 'symbol' || equationItem.type == 'value') {
+									value += equationItem.value;
+								} else if (equationItem.type == 'id') {
+									value += Number(data[equationItem.value]) || 0;
+								}
+							});
+							if (units[key]?.decimalPlace) {
+								if (units[key]?.takeValueMethod) {
+									value =
+										units[key]?.takeValueMethod == 1 ?
+										parseFloat(eval(value).toFixed(units[key].decimalPlace)) :
+										this.truncateToFixedManual(eval(value), units[key].decimalPlace);
+								} else {
+									value = parseFloat(eval(value).toFixed(units[key].decimalPlace));
+								}
+							} else {
+								value = parseFloat(eval(value).toFixed(2));
+							}
+							if (this.$refs['customTextRef' + item.id] && this.$refs['customTextRef' + item.id][
+									0
+								]) {
+								this.$refs['customTextRef' + item.id][0].equationValue({
+									domId: key,
+									value
+								});
+							}
+						}
+					}
+				});
+			},
+
+			truncateToFixedManual(num, decimalPlaces) {
+				let factor = Math.pow(10, decimalPlaces);
+				return Math.floor(num * factor) / factor;
+			},
+
+			getObjValue() {
+				this.getValue();
+				let data = {};
+				let units = {};
+				this.list.forEach(item => {
+					units = {
+						...item.units,
+						...units
+					};
+
+
+					if (item.type == 'customText') {
+						data = {
+							...item.valueObj,
+							...data
+						};
+					} else {
+						item.valueObj.columns.forEach((row) => {
+							row.forEach((cell) => {
+								data[cell.id] = cell.value;
+							});
+						});
+					}
+
+
+				});
+				return {
+					data: data || {},
+					units: units || {}
+				};
+			},
+
+
+
+
+
+
+
+
+			// 初始化
+			init(list) {
+				if (!list) return;
+
+				this.list = JSON.parse(list);
+
+				if (this.list.length) {
+					this.$nextTick(() => {
+						setTimeout(() => {
+							this.list.forEach(item => {
+
+								if (this.$refs['customTextRef' + item.id] && this.$refs[
+										'customTextRef' + item.id][0]) {
+									this.$refs['customTextRef' + item.id][0].init({
+										form: item.value,
+										valueObj: item.valueObj,
+										equation: item.equation,
+										units: item.units
+									});
+								}
+							});
+						}, 1200)
+
+					});
+				}
+			},
+
+
+
+
+
+
+
+
+
+			// 获取值
+			getValue() {
+				this.list.forEach((item, index) => {
+
+					if (this.$refs['customTextRef' + item.id] && this.$refs['customTextRef' + item.id][0]) {
+						let {
+							form,
+							valueObj,
+							equation,
+							units
+						} = this.$refs['customTextRef' + item.id][0].getValue();
+						this.$set(this.list[index], 'value', form);
+						this.$set(this.list[index], 'valueObj', valueObj);
+						this.$set(this.list[index], 'equation', equation);
+						this.$set(this.list[index], 'units', units);
+					}
+				});
+				return this.list;
+			},
+
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.experiment-process-container {
+		width: 100%;
+		overflow: hidden;
+
+		.toolbar {
+			display: flex;
+			padding: 20rpx;
+			background-color: #fff;
+			border-bottom: 1rpx solid #e0e0e0;
+
+			.toolbar-btn {
+				flex: 1;
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				justify-content: center;
+				padding: 20rpx;
+				margin: 0 10rpx;
+				background-color: #f5f5f5;
+				border-radius: 8rpx;
+
+				&:active {
+					background-color: #e0e0e0;
+				}
+
+				.btn-icon {
+					font-size: 40rpx;
+					margin-bottom: 10rpx;
+				}
+
+				.btn-text {
+					font-size: 24rpx;
+					color: #333;
+				}
+			}
+		}
+
+		.drag-list {
+			padding: 20rpx;
+			min-height: 400rpx;
+
+			.list-item {
+				position: relative;
+				margin-bottom: 20rpx;
+				background-color: #fff;
+				border: 1rpx solid #e0e0e0;
+				border-radius: 8rpx;
+				overflow: hidden;
+
+				.item-actions {
+					display: flex;
+					justify-content: flex-end;
+					padding: 10rpx;
+					background-color: #f9f9f9;
+					border-bottom: 1rpx solid #e0e0e0;
+
+					.action-btn {
+						width: 60rpx;
+						height: 60rpx;
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						border-radius: 50%;
+						margin-left: 10rpx;
+
+						.icon {
+							font-size: 32rpx;
+							color: #fff;
+						}
+
+						&.drag-handle {
+							background-color: #409eff;
+						}
+
+						&.delete-btn {
+							background-color: #f56c6c;
+						}
+					}
+				}
+
+				.item-content {
+					padding: 20rpx;
+				}
+			}
+
+			&.is-readonly {
+				.item-actions {
+					display: none;
+				}
+			}
+		}
+
+		// 配置面板
+		.config-panel {
+			background-color: #fff;
+			border-radius: 20rpx 20rpx 0 0;
+			padding: 30rpx;
+
+			.panel-header {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+				padding-bottom: 30rpx;
+				border-bottom: 1rpx solid #e0e0e0;
+
+				.header-title {
+					font-size: 36rpx;
+					font-weight: bold;
+					color: #333;
+				}
+
+				.close-btn {
+					width: 60rpx;
+					height: 60rpx;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+
+					.close-icon {
+						font-size: 60rpx;
+						color: #999;
+						line-height: 1;
+					}
+				}
+			}
+
+			.panel-body {
+				padding: 30rpx 0;
+
+				.form-item {
+					margin-bottom: 30rpx;
+
+					.label {
+						display: block;
+						font-size: 28rpx;
+						color: #333;
+						margin-bottom: 15rpx;
+					}
+
+					.form-input {
+						width: 100%;
+						height: 80rpx;
+						padding: 0 20rpx;
+						border: 1rpx solid #e0e0e0;
+						border-radius: 8rpx;
+						font-size: 28rpx;
+
+						&:focus {
+							border-color: #157a2c;
+						}
+					}
+
+					.picker-value {
+						height: 80rpx;
+						line-height: 80rpx;
+						padding: 0 20rpx;
+						border: 1rpx solid #e0e0e0;
+						border-radius: 8rpx;
+						font-size: 28rpx;
+						background-color: #fff;
+					}
+
+					.formula-display {
+						padding: 20rpx;
+						background-color: #f5f5f5;
+						border-radius: 8rpx;
+						margin-bottom: 15rpx;
+
+						.formula-text {
+							font-size: 28rpx;
+							color: #333;
+							word-break: break-all;
+						}
+					}
+
+					.formula-actions {
+						display: flex;
+						gap: 20rpx;
+
+						.action-btn {
+							flex: 1;
+							height: 70rpx;
+							line-height: 70rpx;
+							text-align: center;
+							border-radius: 8rpx;
+							font-size: 28rpx;
+
+							&.primary {
+								background-color: #157a2c;
+								color: #fff;
+							}
+
+							&.danger {
+								background-color: #f56c6c;
+								color: #fff;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		// 公式弹窗
+		.equation-popup {
+			width: 650rpx;
+			max-height: 80vh;
+			background-color: #fff;
+			border-radius: 16rpx;
+			overflow: hidden;
+
+			.popup-header {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+				padding: 30rpx;
+				border-bottom: 1rpx solid #e0e0e0;
+
+				.header-title {
+					font-size: 32rpx;
+					font-weight: bold;
+					color: #333;
+				}
+
+				.close-btn {
+					width: 50rpx;
+					height: 50rpx;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+
+					.close-icon {
+						font-size: 50rpx;
+						color: #999;
+						line-height: 1;
+					}
+				}
+			}
+
+			.popup-body {
+				padding: 30rpx;
+				max-height: 60vh;
+				overflow-y: auto;
+
+				.formula-selects {
+					display: flex;
+					gap: 20rpx;
+					margin-bottom: 20rpx;
+
+					.select-btn {
+						flex: 1;
+						height: 70rpx;
+						line-height: 70rpx;
+						padding: 0 20rpx;
+						border: 1rpx solid #e0e0e0;
+						border-radius: 8rpx;
+						display: flex;
+						justify-content: space-between;
+						align-items: center;
+						background-color: #fff;
+						font-size: 26rpx;
+						color: #333;
+
+						.arrow {
+							color: #999;
+						}
+					}
+				}
+
+				.value-input {
+					display: flex;
+					gap: 20rpx;
+					margin-bottom: 20rpx;
+
+					.form-input {
+						flex: 1;
+						height: 70rpx;
+						padding: 0 20rpx;
+						border: 1rpx solid #e0e0e0;
+						border-radius: 8rpx;
+						font-size: 28rpx;
+					}
+
+					.confirm-btn {
+						width: 120rpx;
+						height: 70rpx;
+						line-height: 70rpx;
+						text-align: center;
+						background-color: #157a2c;
+						color: #fff;
+						border-radius: 8rpx;
+						font-size: 28rpx;
+					}
+				}
+
+				.equation-tags {
+					display: flex;
+					flex-wrap: wrap;
+					gap: 15rpx;
+					margin-bottom: 20rpx;
+					min-height: 80rpx;
+					padding: 20rpx;
+					background-color: #f5f5f5;
+					border-radius: 8rpx;
+
+					.tag {
+						position: relative;
+						display: flex;
+						align-items: center;
+						padding: 10rpx 20rpx;
+						background-color: #e0e0e0;
+						border-radius: 20rpx;
+						font-size: 26rpx;
+						color: #333;
+
+						&.active {
+							background-color: #157a2c;
+							color: #fff;
+						}
+
+						.close {
+							margin-left: 10rpx;
+							padding: 0 5rpx;
+							font-size: 32rpx;
+						}
+					}
+				}
+
+				.formula-preview {
+					padding: 20rpx;
+					background-color: #f9f9f9;
+					border-radius: 8rpx;
+
+					.preview-text {
+						font-size: 28rpx;
+						color: #333;
+						word-break: break-all;
+					}
+				}
+			}
+
+			.popup-footer {
+				display: flex;
+				gap: 20rpx;
+				padding: 20rpx 30rpx 30rpx;
+				border-top: 1rpx solid #e0e0e0;
+
+				.footer-btn {
+					flex: 1;
+					height: 80rpx;
+					line-height: 80rpx;
+					text-align: center;
+					border-radius: 8rpx;
+					font-size: 32rpx;
+
+					.btn-text {
+						color: #fff;
+					}
+
+					&.cancel {
+						background-color: #e0e0e0;
+
+						.btn-text {
+							color: #333;
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 1 - 1
manifest.json

@@ -2,7 +2,7 @@
     "name" : "智慧工厂",
     "appid" : "__UNI__45B3907",
     "description" : "",
-    "versionName" : "V1.0.3.46",
+    "versionName" : "V1.0.3.47",
     "versionCode" : "100",
     "transformPx" : false,
     "h5" : {

+ 1 - 1
pages/home/home.vue

@@ -268,7 +268,7 @@
 			uni.getStorage({
 				key: 'userInfo',
 				success: res => {
-					console.log(res);
+					// console.log(res);
 					this.roleId = res.data.roleId[0]
 				}
 			})

+ 7 - 7
pages/index/index.vue

@@ -308,8 +308,8 @@ export default {
       statistics()
         .then((res) => {
           this.workOrder = res;
-          console.log("this.operationsList---", this.operationsList);
-          console.log("res", res);
+          // console.log("this.operationsList---", this.operationsList);
+          // console.log("res", res);
           this.operationsList.forEach((item) => {
             if (item.name == "保养工单") item.badge = res.maintenanceNum;
             if (item.name == "巡点检工单") item.badge = res.patrolInspection;
@@ -328,15 +328,15 @@ export default {
       console.log("-----------list--------------");
       console.log(list);
       if (list[0] && list[0].children.length > 0) {
-        console.log(list[0].children, "list[0].children ----");
+        // console.log(list[0].children, "list[0].children ----");
         list[0].children.forEach((f) => {
           if (f.path == "productionManage") {
             this.productionList = f.children;
           } else if (f.path == "operationsList") {
             statistics().then((res) => {
               this.workOrder = res;
-              console.log("this.operationsList---", this.operationsList);
-              console.log("res", res);
+              // console.log("this.operationsList---", this.operationsList);
+              // console.log("res", res);
               f.children.forEach((item) => {
                 if (item.name == "保养工单") item.badge = res.maintenanceNum;
                 if (item.name == "巡点检工单")
@@ -354,7 +354,7 @@ export default {
               item.name = item.name.replace("管理员", "");
             });
           } else if (f.path == "internalManagement") {
-            console.log(f, "f");
+            // console.log(f, "f");
             this.internalManagementList = f.children;
           } else if (f.path == "serviceList") {
             this.serviceList = f.children;
@@ -372,7 +372,7 @@ export default {
     },
 
     toNav(url) {
-      console.log(url);
+      // console.log(url);
       uni.navigateTo({
         url: url,
       });

+ 1 - 1
pages/maintenance/check/components/PreviewPhoto.vue

@@ -51,7 +51,7 @@
 				// 选择要查询的元素
 				query.select('.container')
 					.boundingClientRect(data => {
-						console.log(data.width,'data.width')
+						// console.log(data.width,'data.width')
 						let height = Math.floor(((data.width - 20) * 31) / 50);
 						this.height = height > 160 ? height + 'rpx' : '180rpx'
 					})

+ 69 - 10
pages/qms/inspectionWork/experimentReport.vue

@@ -104,8 +104,11 @@
 						工艺要求
 					</view>
 				</view>
-				<u--textarea class="marginTop20" disabled placeholder=" " border="surround" autoHeight
-					v-model="form.processRequirementsJson"></u--textarea>
+				<view class="marginTop20 defValue">
+
+					{{ form.processRequirementsJson}}
+				</view>
+
 
 				<view class="herder_item marginTop20">
 					<view class="herder_text"></view>
@@ -132,6 +135,20 @@
 							style="margin-right: 10rpx;"></u-icon> {{type=='view'?'查看实验耗材':'添加实验耗材'}}
 					</view>
 				</view>
+				<view class="herder_item marginTop20">
+					<view class="herder_text"></view>
+					<view class="herder_view">
+						实验过程
+					</view>
+				</view>
+				<experimentationProcess ref="experimentationProcess1" :readonly="type=='view'" class="marginTop20"></experimentationProcess>
+				<view class="herder_item marginTop20">
+					<view class="herder_text"></view>
+					<view class="herder_view">
+						实验记录
+					</view>
+				</view>
+				<experimentationProcess ref="experimentationProcess2" :readonly="type=='view'" class="marginTop20"></experimentationProcess>
 				<view class="herder_item marginTop20">
 					<view class="herder_text"></view>
 					<view class="herder_view">
@@ -146,7 +163,8 @@
 						实验结论
 					</view>
 				</view>
-				<uni-data-picker v-if="type!='view'" class="marginTop20" v-model="form.results" placeholder="请选择" :localdata="[{
+				<uni-data-picker v-if="type!='view'" class="marginTop20" v-model="form.results" placeholder="请选择"
+					:localdata="[{
 					text:'合格',value:1
 				},{
 					text:'不合格',value:2
@@ -246,11 +264,14 @@
 	} from '@/api/inspectionWork/index.js'
 	import PreviewPhoto from '@/pages/maintenance/check/components/PreviewPhoto.vue'
 	import myCard from '@/pages/saleManage/components/myCard.vue'
+	import experimentationProcess from '@/components/templateDiv/experimentationProcess.vue'
+
 	export default {
 		mixins: [dictMixns],
 		components: {
 			PreviewPhoto,
-			myCard
+			myCard,
+			experimentationProcess
 		},
 		props: {
 			type: {
@@ -432,6 +453,8 @@
 					this.form.qualityStandardName = row.qualityStandardName;
 					this.form.qualityStandardId = row.qualityStandardId;
 					this.form.qualityWorkOrderId = row.qualityWorkOrderId;
+					this.form.recordJson = row.recordJson;
+					this.form.procedureJson = row.procedureJson;
 					if (row.symbol) {
 						this.form.processRequirementsJson += row.symbol;
 					}
@@ -458,7 +481,19 @@
 					})
 				})
 				this.show = true
-
+				this.$nextTick(() => {
+					// console.log(this.form,'this.form')
+					if (this.form.procedureJson) {
+						this.$refs.experimentationProcess1.init(
+							this.form.procedureJson.tempJson
+						);
+					}
+					if (this.form.recordJson) {
+						this.$refs.experimentationProcess2.init(
+							this.form.recordJson.tempJson
+						);
+					}
+				});
 
 				// this.init()
 			},
@@ -468,7 +503,6 @@
 				uni.chooseImage({
 					count: 9, //默认9
 					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
-					sourceType: ['camera'], //从相册选择
 					success: function(res) {
 						uni.showLoading({
 							title: '加载中'
@@ -545,11 +579,26 @@
 				// 	}, )
 				// 	return
 				// }
-				let api = this.form.id ? updateExperiment : saveExperiment
-				api(this.form)
+				let data = JSON.parse(JSON.stringify(this.form));
+				let procedureJson = this.$refs.experimentationProcess1.getValue();
+				let recordJson = this.$refs.experimentationProcess2.getValue();
+				if (procedureJson) {
+					data.procedureJson = {
+						tempJson: JSON.stringify(procedureJson)
+					};
+				}
+				if (recordJson) {
+					data.recordJson = {
+						tempJson: JSON.stringify(recordJson)
+					};
+				}
+				let api = data.id ? updateExperiment : saveExperiment
+				// console.log(data,'data')
+				// return
+				api(data)
 					.then((msg) => {
 						this.$emit('experimentReportSuccess', {
-							workId: this.form.qualityWorkOrderId,
+							workId: data.qualityWorkOrderId,
 							projectId: this.projectData.id,
 						})
 						this.cancel()
@@ -592,8 +641,18 @@
 		}
 	}
 
+	.defValue {
+		color: #c1353c;
+		text-indent: 2ch;
+	}
+
 	/deep/.uni-textarea {
-		padding: 0
+		padding: 0;
+		max-height: auto;
+	}
+
+	/deep/uni-textarea {
+		max-height: auto;
 	}
 
 	/deep/.input-value {

+ 20 - 4
pages/qms/inspectionWork/inspectionProjectReport.vue

@@ -149,6 +149,20 @@
 				class="marginTop20" placeholder="请输入内容" border="surround" v-model="form.retainedSampleUnqualified">
 				<template slot="suffix" v-if="form.measureUnit">{{form.measureUnit}}</template>
 			</u-input>
+			<view class="herder_item marginTop20">
+				<view class="herder_text"></view>
+				<view class="herder_view">
+					检验图片
+				</view>
+			</view>
+			<fileMain class="marginTop20" v-model="form.inspectImage" :type="type=='view'?'view':'add'" ></fileMain>
+			<view class="herder_item marginTop20">
+				<view class="herder_text"></view>
+				<view class="herder_view">
+					附件
+				</view>
+			</view>
+			<fileMain class="marginTop20" v-model="form.imgUrl" type="view" ></fileMain>
 			<view class="herder_item marginTop20">
 				<view class="herder_text"></view>
 				<view class="herder_view">
@@ -178,6 +192,7 @@
 <script>
 	import dictMixns from '@/mixins/dictMixins'
 	import experimentReport from './experimentReport.vue'
+	import fileMain from "@/pages/doc/index.vue";
 	import {
 		getById,
 		exeReportWorkBySingleTemplate,
@@ -193,14 +208,14 @@
 
 	export default {
 		components: {
-			experimentReport
+			experimentReport,fileMain
 		},
 		mixins: [dictMixns],
 		data() {
 			return {
 				handleInput,
 				show: false,
-				type: 'edit',
+				type: 'view',
 				form: {
 					lossNumber: '',
 					lossNumberUnqualified: '',
@@ -209,7 +224,8 @@
 					qualifiedQuantity: '',
 					noQualifiedQuantity: '',
 					sampleQuantity: '',
-					experimentId: ''
+					experimentId: '',
+					inspectImage:[]
 				},
 				acceptUnpackList: [{
 						text: "合格",
@@ -280,7 +296,7 @@
 				this.$refs.experimentReportRef.open({
 					...this.form,
 					qualityWorkOrderId: this.workData.id
-				}, 'add')
+				})
 			},
 
 			async exeReportWorkBySingleTemplate() {

+ 1 - 1
store/modules/app.js

@@ -4,7 +4,7 @@ const state = {
 
 const mutations = {
   RESET_SCANCODEDATE (state) {
-    console.log('RESET_SCANCODEDATE')
+    // console.log('RESET_SCANCODEDATE')
     state.scancodedate = 'scancodedate'
   },
   SET_SCANCODEDATE (state, name = 'scancodedate') {

+ 1 - 1
store/modules/dict.js

@@ -35,7 +35,7 @@ const mutations = {
 		code,
 		dict
 	}) => {
-		console.log(dict, 'dict1')
+		// console.log(dict, 'dict1')
 		Vue.set(state, code, dict)
 	}
 

+ 1 - 1
utils/request.js

@@ -37,7 +37,7 @@ const http = ({
 			timeout: 600000,
 			...other,
 			complete: (res) => {
-				console.log(res,'res')
+				// console.log(res,'res')
 				if (showLoading) {
 					uni.hideLoading();
 				}