|
|
@@ -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>
|