yangg 6 місяців тому
батько
коміт
b516282fcb

+ 335 - 70
ui/src/views/system/document/com/components/TextArea/index.vue

@@ -136,7 +136,7 @@ export default {
       handler(val) {
         if (val == null || this.isEdit != 1) return;
         
-        this.$nextTick(() => {
+        this.$nextTick(async () => {
           try {
             if (!this.$refs.wordEditor) {
               console.warn('编辑器未就绪');
@@ -149,6 +149,73 @@ export default {
             let currentContent = this.com.content || '';
             let insertContent = val.content;
 
+            // 如果是规格属性,先进行预处理
+            if (val.type === 'ProductAttr') {
+              // 构建规格HTML
+              const specs = val.attrs?.specifications?.[0];
+              if (specs) {
+                const prodAttrId = `${val.id}_${this.com.attrs?.length || 0}`;
+                
+                let specHtml = `
+                  <div class="product-attr-container">
+                    <div class="spec-label">${specs.psp_name || '规格'}:</div>
+                    <div class="spec-input">
+                `;
+
+                switch (specs.psp_type) {
+                  case '单选':
+                    const options = specs.psp_value ? specs.psp_value.split(',') : [];
+                    specHtml += `
+                      <select 
+                        id="${prodAttrId}" 
+                        data-index="${this.com.attrs?.length || 0}"
+                        class="text-input-box product-select"
+                        style="min-width: 120px;"
+                      >
+                        <option value="">请选择</option>
+                        ${options.map(opt => `<option value="${opt}">${opt}</option>`).join('')}
+                      </select>
+                    `;
+                    break;
+
+                  case '多选':
+                    const multiOptions = specs.psp_value ? specs.psp_value.split(',') : [];
+                    specHtml += `
+                      <select 
+                        id="${prodAttrId}" 
+                        data-index="${this.com.attrs?.length || 0}"
+                        class="text-input-box product-select" 
+                        multiple
+                        style="min-width: 120px;"
+                      >
+                        ${multiOptions.map(opt => `<option value="${opt}">${opt}</option>`).join('')}
+                      </select>
+                    `;
+                    break;
+
+                  default:
+                    specHtml += `
+                      <input 
+                        type="text" 
+                        id="${prodAttrId}" 
+                        data-index="${this.com.attrs?.length || 0}"
+                        class="text-input-box auto-width" 
+                        value="${specs.ps_name||0}"
+                        placeholder="请输入${specs.psp_name || ''}"
+                        style="min-width: 120px;"
+                      >
+                    `;
+                }
+
+                specHtml += `
+                    </div>
+                  </div>
+                `;
+                
+                insertContent = specHtml;
+              }
+            }
+
             // 确保变量两侧有空格
             if (insertContent.includes('{{') && insertContent.includes('}}')) {
               insertContent = ` ${insertContent} `;
@@ -156,23 +223,16 @@ export default {
 
             // 构建新内容
             let newContent;
-            
-            // 如果编辑器支持选区
             if (editor.getSelection) {
               const selection = editor.getSelection();
               if (selection) {
-                // 替换选中的内容
-                const start = selection.start || 0;
-                const end = selection.end || start;
-                newContent = currentContent.substring(0, start) + 
-                           insertContent + 
-                           currentContent.substring(end);
+                newContent = currentContent.substring(0, selection.start) + 
+                            insertContent + 
+                            currentContent.substring(selection.end);
               } else {
-                // 在末尾添加
                 newContent = currentContent + insertContent;
               }
             } else {
-              // 在末尾添加
               newContent = currentContent + insertContent;
             }
 
@@ -187,31 +247,19 @@ export default {
             // 强制重新渲染
             this.keys = new Date().getTime();
 
-            // 调用保存方法
-            this.save({
+            // 保存更新
+            await this.save({
               main: newContent
             });
 
-            // 确保视图更新
-            this.$forceUpdate();
-            
-            // 同步到预览模式
-            this.syncContent();
-
-            // 记录操作日志
-            console.log('内容插入成功:', {
-              原内容: currentContent,
-              插入内容: insertContent,
-              新内容: newContent
+            // 确保视图更新并绑定事件
+            this.$nextTick(() => {
+              this.bindProductAttrEvents();
+              this.$forceUpdate();
             });
 
           } catch (error) {
             console.error('插入内容时出错:', error);
-            console.error('错误详情:', {
-              编辑器: this.$refs.wordEditor,
-              内容: val.content,
-              当前内容: this.com.content
-            });
           }
         });
       },
@@ -244,12 +292,15 @@ export default {
     this.$nextTick(() => {
       this.initializeInputWidths();
       this.syncContent();
+      this.bindProductAttrEvents();
     });
   },
   updated() {
     this.$nextTick(() => {
       if (this.isEdit == '2') {
         this.syncContent();
+      } else {
+        this.bindProductAttrEvents();
       }
     });
   },
@@ -258,6 +309,10 @@ export default {
     this.$el.removeEventListener("input", this.handleInputChange);
     this.$el.removeEventListener("input", this.handleVariableNullInput);
     this.$el.removeEventListener("blur", this.handleVariableNullBlur, true);
+    const inputs = this.$el.querySelectorAll('.text-input-box');
+    inputs.forEach(input => {
+      input.removeEventListener('change', this.handleProductAttrChange);
+    });
   },
   methods: {
     /*canvas 添加 */
@@ -374,53 +429,100 @@ export default {
               '">'
           );
         } else if (_this.com.attrs[i].type == "ProductAttr") {
-          //处理产品属性值
           let item = _this.com.attrs[i];
           let prodAttrId = item.id + "_" + i;
-          if (item.content == "") {
-            item.content = item.attrs.value;
+          
+          // 添加调试日志
+          console.log('ProductAttr item:', item);
+          console.log('ProductAttr specifications:', item.attrs?.specifications);
+          
+          // 获取规格信息
+          const specs = item.attrs?.specifications?.[0];
+          if (!specs) {
+            console.warn('No specifications found for ProductAttr:', item);
+            continue;
           }
-          if (item.attrs.type == 1) {
-            //输入框
-            data = data.replace(
-              "{{" + item.id + "}}",
-              '<input type="text" id="' +
-                prodAttrId +
-                '"  data-index="' +
-                i +
-                '" class="text-input-box" value="' +
-                item.content +
-                '">'
-            );
+
+          // 添加规格名称显示
+          let specHtml = `
+            <div class="product-attr-container">
+              <div class="spec-label">${specs.psp_name || '规格'}:</div>
+              <div class="spec-input">
+          `;
+
+          // 根据psp_type决定渲染什么类型的输入控件
+          if (this.isEdit == '1') {
+            // 预览模式直接显示内容
+            specHtml += `<span class="preview-content">${item.content || ''}</span>`;
           } else {
-            //选择框
-            let dataItem = item.attrs.valueItems.split(",");
-            let selectHtml =
-              '<select id="' +
-              prodAttrId +
-              '"  data-index="' +
-              i +
-              '" class="text-input-box">';
-            for (var l = 0; l < dataItem.length; l++) {
-              if (item.content == dataItem[l]) {
-                selectHtml +=
-                  '<option value="' +
-                  dataItem[l] +
-                  '" selected>' +
-                  dataItem[l] +
-                  "</option>";
-              } else {
-                selectHtml +=
-                  '<option value="' +
-                  dataItem[l] +
-                  '">' +
-                  dataItem[l] +
-                  "</option>";
-              }
+            console.log(specs.psp_type);
+            switch (specs.psp_type) {
+              /* case '2': // 单选 */
+              case '单选':
+                // 处理单选下拉框
+                const options = specs.psp_value ? specs.psp_value.split(',') : [];
+                specHtml += `
+                  <select 
+                    id="${prodAttrId}" 
+                    data-index="${i}" 
+                    class="text-input-box product-select"
+                    style="min-width: 120px;"
+                  >
+                    <option value="">请选择</option>
+                    ${options.map(opt => `
+                      <option 
+                        value="${opt}" 
+                        ${item.content === opt ? 'selected' : ''}
+                      >${opt}</option>
+                    `).join('')}
+                  </select>
+                `;
+                break;
+
+             /*  case '3': // 多选  */
+              case '多选':
+                // 处理多选下拉框
+                const multiOptions = specs.psp_value ? specs.psp_value.split(',') : [];
+                const selectedValues = specs.ps_name ? specs.ps_name.split(',') : [];
+                specHtml += `
+                  <select 
+                    id="${prodAttrId}" 
+                    data-index="${i}" 
+                    class="text-input-box product-select" 
+                    multiple
+                    style="min-width: 120px;"
+                  >
+                    ${multiOptions.map(opt => `
+                      <option 
+                        value="${opt}" 
+                        ${selectedValues.includes(opt) ? 'selected' : ''}
+                      >${opt}</option>
+                    `).join('')}
+                  </select>
+                `;
+                break;
+
+              default: // 输入框
+                specHtml += `
+                  <input  
+                    id="${prodAttrId}" 
+                    data-index="${i}" 
+                    class="text-input-box product-input" 
+                    value="${item.content || 0}"
+                    placeholder="请输入${specs.psp_name || ''}"
+                    style="min-width: 120px;"
+                  >
+                `;
             }
-            selectHtml += "</select>";
-            data = data.replace("{{" + item.id + "}}", selectHtml);
           }
+
+          specHtml += `
+              </div>
+            </div>
+          `;
+
+          // 替换标记
+          data = data.replace("{{" + item.id + "}}", specHtml);
         } else if (_this.com.attrs[i].type == "formual") {
           //处理计算公式 不处理
           let formual = await _this.analysisFormual(_this.com.attrs[i]);
@@ -1156,6 +1258,72 @@ export default {
       // 保留空格和换行
       return content.replace(/\s+/g, ' ').replace(/\n/g, '<br>');
     },
+
+    handleProductAttrChange(event) {
+      const target = event.target;
+      const index = parseInt(target.dataset.index);
+      const item = this.com.attrs[index];
+      
+      console.log('ProductAttr change:', {
+        target,
+        index,
+        item
+      });
+      
+      if (!item || item.type !== 'ProductAttr') return;
+      
+      let newValue;
+      if (target.multiple) {
+        // 处理多选
+        newValue = Array.from(target.selectedOptions)
+          .map(opt => opt.value)
+          .filter(Boolean)
+          .join(',');
+      } else {
+        newValue = target.value;
+      }
+      
+      // 更新内容
+      this.$set(item, 'content', newValue);
+      
+      // 触发更新事件
+      this.$emit('onUpdateProdAttr', this.currentIndex, index, newValue);
+    },
+
+    handleProductAttrInput(event) {
+      // 处理输入框实时输入
+      const target = event.target;
+      if (target.classList.contains('product-input')) {
+        this.adjustInputWidth({ target });
+      }
+    },
+
+    bindProductAttrEvents() {
+      // 移除编辑模式判断,允许在所有模式下绑定事件
+      this.$nextTick(() => {
+        const inputs = this.$el.querySelectorAll('.product-input, .product-select');
+        console.log('Found product inputs:', inputs.length);
+        
+        inputs.forEach(input => {
+          // 移除旧的事件监听
+          input.removeEventListener('change', this.handleProductAttrChange);
+          input.removeEventListener('input', this.handleProductAttrInput);
+          input.removeEventListener('blur', this.handleProductAttrChange);
+          
+          // 确保输入控件是可交互的
+          input.removeAttribute('readonly');
+          input.removeAttribute('disabled');
+          
+          // 添加新的事件监听
+          if (input.tagName.toLowerCase() === 'select') {
+            input.addEventListener('change', this.handleProductAttrChange);
+          } else {
+            input.addEventListener('input', this.handleProductAttrInput);
+            input.addEventListener('blur', this.handleProductAttrChange);
+          }
+        });
+      });
+    }
   },
 };
 </script>
@@ -1309,4 +1477,101 @@ export default {
     }
   }
 }
+
+::v-deep {
+  .product-attr-container {
+    display: inline-flex;
+    align-items: center;
+    // 调整容器高度和间距
+    min-height: 28px;
+    margin: 2px 4px;
+    vertical-align: middle;
+
+    .spec-label {
+      margin-right: 8px;
+      white-space: nowrap;
+      font-size: 14px;
+      line-height: 1.4;
+    }
+
+    .spec-input {
+      flex: 1;
+      min-width: 120px;
+      display: inline-flex;
+      align-items: center;
+    }
+  }
+
+  .product-input,
+  .product-select {
+    // 调整输入框和选择框的尺寸
+    height: 28px;
+    line-height: 26px;
+    padding: 0 8px;
+    margin: 0;
+    font-size: 14px;
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    color: #606266;
+    background-color: #fff;
+    transition: all 0.2s;
+    
+    &:hover {
+      border-color: #c0c4cc;
+    }
+    
+    &:focus {
+      border-color: #409eff;
+      outline: none;
+    }
+  }
+
+  .product-select {
+    padding-right: 25px;
+    
+    &[multiple] {
+      // 调整多选下拉框的高度
+      height: auto;
+      min-height: 28px;
+      max-height: 120px;
+      padding: 2px;
+      
+      option {
+        padding: 4px 8px;
+        line-height: 20px;
+        
+        &:checked {
+          background-color: #409eff;
+          color: #fff;
+        }
+      }
+    }
+  }
+
+  // 预览模式样式优化
+  .view-mode {
+    .product-attr-container {
+      margin: 0 2px;
+    }
+
+    .product-input,
+    .product-select {
+      background-color: transparent;
+      min-width: 80px;
+      
+      &:not(:focus) {
+        border-color: transparent;
+      }
+      
+      &:hover {
+        border-color: #dcdfe6;
+      }
+      
+      &:focus {
+        background-color: #fff;
+        border-color: #409eff;
+      }
+    }
+  }
+}
 </style>

+ 263 - 109
ui/src/views/system/document/com/components/codeJson/index.vue

@@ -3,86 +3,60 @@
     <div class="product-content">
       <div class="content-header">
         <div class="search-area">
-          <el-form :inline="true" :model="searchForm" ref="searchForm">
+          <el-form :inline="true" :model="queryForm" ref="searchForm">
             <el-form-item label="产品应用编号">
-              <el-input
-                v-model="searchForm.dcp_p_no"
-                placeholder="请输入产品应用编号"
-                clearable
-              ></el-input>
-            </el-form-item>
-            <el-form-item label="产品名称">
-              <el-input
-                v-model="searchForm.dcp_name"
-                placeholder="请输入产品名称"
-                clearable
-              ></el-input>
-            </el-form-item>
-            <el-form-item label="产品类型">
-              <el-input
-                v-model="searchForm.dcp_model"
-                placeholder="请输入产品类型"
-                clearable
-              ></el-input>
-            </el-form-item>
-            <el-form-item label="OA编号">
-              <el-input
-                v-model="searchForm.dcp_oa_code"
-                placeholder="请输入OA编号"
-                clearable
-              ></el-input>
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" @click="handleSearch">搜索</el-button>
-              <el-button @click="resetForm">重置</el-button>
+              <el-select 
+                v-model="queryForm.type" 
+                placeholder="请选择产品"
+                @change="handleTypeChange"
+              >
+                <el-option
+                  v-for="(item, index) in productList"
+                  :key="index"
+                  :value="item.dcp_id"
+                  :label="item.dcp_name"
+                ></el-option>
+              </el-select>
             </el-form-item>
           </el-form>
         </div>
-        <!--   <el-button type="primary" @click="handleAddProduct">新建产品</el-button> -->
       </div>
 
-      <el-table :data="productList">
-        <el-table-column prop="dcp_oa_code" label="OA编号" />
-        <el-table-column
-          prop="dcp_p_no"
-          label="产品应用编号"
-          show-overflow-tooltip
-          :min-width="120"
+      <!-- 修改JSON数据显示部分 -->
+      <div v-if="parsedSpecifications.length" class="specifications-list">
+        <el-card 
+          v-for="(spec, index) in parsedSpecifications" 
+          :key="index" 
+          class="spec-card"
         >
-          <template slot-scope="scope">
-            <div class="ellipsis">{{ scope.row.dcp_p_no }}</div>
-          </template>
-        </el-table-column>
-        <el-table-column prop="dcp_name" label="产品名称" />
-        <el-table-column prop="dcp_model" label="产品类型" />
-        <!--<el-table-column prop="screenMaterial" label="屏幕材质" /> -->
-        <el-table-column label="操作" width="200">
-          <template slot-scope="scope">
-            <el-button size="mini" @click="handleUseProduct(scope.row)"
-              >使用</el-button
+          <div class="spec-header">
+            <span class="spec-title">{{ spec.psp_name  }}</span>
+            <el-button 
+              type="primary" 
+              size="small" 
+              @click="handleUseSpec(spec)"
             >
-            <!-- <el-button size="mini" @click="handleEditProduct(scope.row)"
-              >编辑</el-button
+              使用此规格
+            </el-button>
+          </div>
+          <div class="spec-content">
+            <el-tag 
+              size="small"
+              type="info"
+              class="spec-tag"
             >
-            <el-button
-              size="mini"
-              type="danger"
-              @click="handleDeleteProduct(scope.row)"
-              >删除</el-button
-            > -->
-          </template>
-        </el-table-column>
-      </el-table>
-      <!-- 添加分页组件 -->
-      <div class="pagination-container">
-        <el-pagination
-          @current-change="handleCurrentChange"
-          :current-page="pagination.currentPage"
-          :page-size="pagination.pageSize"
-          :total="pagination.total"
-          layout="total, prev, pager, next"
-        >
-        </el-pagination>
+              {{ spec.psp_name }}: {{ spec.ps_name || '-' }}
+            </el-tag>
+            <div class="spec-details">
+              <div v-if="spec.psp_type">类型: {{ getTypeText(spec.psp_type) }}</div>
+              <div v-if="spec.psp_options">选项: {{ spec.psp_options }}</div>
+            </div>
+          </div>
+        </el-card>
+      </div>
+
+      <div class="tip-message" v-if="!productList.length">
+        <el-empty description="暂无商品信息"></el-empty>
       </div>
     </div>
   </div>
@@ -100,26 +74,27 @@ export default {
   /*组件状态值*/
   data() {
     return {
-      searchForm: {
-        dcp_p_no: "",
-        dcp_name: "",
-        dcp_model: "",
-        dcp_oa_code: "",
-      },
-      typeList: [],
-      dcpInfo: null,
-      // 分页参数
-      pagination: {
-        currentPage: 1,
-        pageSize: 10,
-        total: 0,
+      queryForm: {
+        type: "", // 选中的产品ID
       },
       productList: [],
+      jsonData: null,
     };
   },
 
   /*计算属性*/
-  computed: {},
+  computed: {
+    parsedSpecifications() {
+      if (!this.jsonData) return [];
+      try {
+        const specs = JSON.parse(this.jsonData);
+        return Array.isArray(specs) ? specs : [];
+      } catch (e) {
+        console.error("解析规格失败:", e);
+        return [];
+      }
+    }
+  },
 
   /*侦听器*/
   watch: {},
@@ -128,47 +103,226 @@ export default {
    * el 被新创建的 vm.$ el 替换,并挂载到实例上去之后调用该钩子。
    * 如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$ el 也在文档内。
    */
-  mounted() {
-    this.fetchProductList();
+  async mounted() {
+    await this.fetchProductList();
   },
 
   /*组件方法*/
   methods: {
-    // 搜索和分页
-    handleSearch() {
-      this.pagination.currentPage = 1; // 重置到第一页
-      this.fetchProductList();
+    // 处理产品选择变化
+    async handleTypeChange(value) {
+      if (value) {
+        await this.fetchDCPPNO(value);
+      } else {
+        this.jsonData = null;
+      }
     },
+
     // 获取产品列表
     async fetchProductList() {
       try {
-        const params = {
-          pageNum: this.pagination.currentPage,
-          pageSize: this.pagination.pageSize,
-          ...this.searchForm, // 展开所有搜索条件
-        };
-
-        productList(params).then((res) => {
-          this.productList = res.rows;
-          this.pagination.total = res.total;
+        const res = await productList({
+          pageNum: 1,
+          pageSize: 999
         });
+        this.productList = res.rows || [];
       } catch (error) {
+        console.error('获取产品列表失败:', error);
         this.$message.error("获取产品列表失败");
       }
     },
-    /* 使用 */
-    handleUseProduct() {},
-    /*  getDCPPNOInfo() {
-      getInfoDCPPNO(this.queryForm.type).then((res) => {
-        this.dcpInfo = res;
-      });
+
+    // 获取指定产品的JSON数据
+    async fetchDCPPNO(id) {
+      try {
+        const res = await getInfoDCPPNO(id);
+        console.log(res);
+        if (res.data && this.isValidJson(res.data.dcppno)) {
+          this.jsonData = res.data.dcppno;
+        } else {
+          this.$message.warning('获取到的JSON数据格式无效');
+          this.jsonData = null;
+        }
+      } catch (error) {
+        console.error('获取DCPPNO数据失败:', error);
+        this.$message.error('获取数据失败');
+        this.jsonData = null;
+      }
     },
 
-    onBtnSearch() {
-      this.getDCPPNOInfo();
-    }, */
+    handleUseSpec(spec) {
+      const selectedProduct = this.productList.find(
+        item => item.dcp_id === this.queryForm.type
+      );
+
+      const safeId = `prod_${Date.now()}_${Math.random()
+        .toString(36)
+        .substr(2, 9)}`;
+
+      const productData = {
+        type: "ProductAttr",
+        id: safeId,
+        content: "",
+        attrs: {
+          specifications: [spec], // 只包含选中的规格
+          label: selectedProduct?.dcp_name || '',
+          productInfo: {
+            dcp_p_no: JSON.stringify([spec]), // 将单个规格转换为数组
+            dcp_name: selectedProduct?.dcp_name || '',
+            dcp_model: selectedProduct?.dcp_model || '',
+            dcp_oa_code: selectedProduct?.dcp_oa_code || '',
+          },
+        },
+      };
+
+      this.$emit("onPicked", productData);
+    },
+
+    // 获取类型的中文释义
+    getTypeText(type) {
+      const typeMap = {
+        1: "输入框",
+        2: "单选框",
+        3: "多选框",
+      };
+      return typeMap[type] || "输入框";
+    },
+
+    // 根据产品规格判断使用什么类型的输入控件
+    determineInputType(row) {
+      if (!row.psp_type) return 1; // 默认为输入框
+
+      switch (parseInt(row.psp_type)) {
+        case 1:
+          return 1; // 输入框
+        case 2:
+          return 2; // 单选框
+        case 3:
+          return 3; // 多选框
+        default:
+          return 1;
+      }
+    },
+
+    // 获取选项值
+    getValueItems(row) {
+      if (!row.psp_options) return "";
+      return row.psp_options;
+    },
+
+    // 检查是否为有效的JSON字符串
+    isValidJson(str) {
+      try {
+        JSON.parse(str);
+        return true;
+      } catch (e) {
+        return false;
+      }
+    },
+
+    // 解析规格信息
+    parseSpecifications(jsonStr) {
+      try {
+        const specs = JSON.parse(jsonStr);
+        return Array.isArray(specs) ? specs : [];
+      } catch (e) {
+        console.error("解析规格失败:", e);
+        return [];
+      }
+    },
   },
 };
 </script>
 <style lang='css' scoped>
+.spec-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 4px;
+  padding: 4px 0;
+}
+
+.spec-tag {
+  margin-right: 4px;
+  margin-bottom: 4px;
+}
+
+.invalid-json {
+  color: #f56c6c;
+  font-size: 12px;
+}
+
+.el-table {
+  margin-top: 15px;
+}
+
+/* 确保长文本正确换行 */
+.el-table .cell {
+  word-break: break-word;
+}
+
+.json-display {
+  margin-top: 20px;
+}
+
+.json-card {
+  margin-bottom: 20px;
+}
+
+.json-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.spec-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+
+.spec-tag {
+  margin-right: 4px;
+  margin-bottom: 4px;
+}
+
+.specifications-list {
+  margin-top: 20px;
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+  gap: 16px;
+}
+
+.spec-card {
+  margin-bottom: 16px;
+}
+
+.spec-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12px;
+}
+
+.spec-title {
+  font-weight: bold;
+  font-size: 14px;
+}
+
+.spec-content {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.spec-details {
+  margin-top: 8px;
+  font-size: 13px;
+  color: #666;
+}
+
+.spec-tag {
+  margin-right: 4px;
+  margin-bottom: 4px;
+}
 </style>

+ 27 - 6
ui/src/views/system/document/com/menus.vue

@@ -145,7 +145,7 @@
       custom-class="prod-verify"
       title="选择"
     >
-      <CodeJson />
+      <CodeJson @onPicked="onPickedProduct" />
     </el-dialog>
   </div>
 </template>
@@ -366,10 +366,12 @@ export default {
         (e.key === "article" &&
           (this.typeMenu === "module" || this.typeMenu === undefined))
       ) {
-        return; // Exit the method if the item is disabled
+        return;
       }
-      /* e.type = "insert"; */
-      if (e.key == "formual") {
+
+      if (e.key == "attr") {
+        this.codeJsonData = true;
+      } else if (e.key == "formual") {
         this.showFormula = true;
       } else if (e.key == "SourceData") {
         this.showSourceData = true;
@@ -389,8 +391,6 @@ export default {
         this.showSourceEs = true;
       } else if (e.key === "Directory") {
         this.showDirectoryLevelDialog = true;
-      } else if (e.key == "attr") {
-        this.codeJsonData = true;
       } else {
         this.$emit("onEvents", e);
       }
@@ -406,6 +406,27 @@ export default {
     getIconPath(iconName) {
       return require(`@/icons/svg/${iconName}.svg`);
     },
+    onPickedProduct(productData) {
+      // 添加数据验证
+      if (!productData || !productData.id) {
+        this.$message.error('商品信息不完整');
+        return;
+      }
+
+      let item = {
+        type: "insert",
+        key: "attr",
+        content: productData
+      };
+
+      try {
+        this.$emit("onEvents", item);
+        this.codeJsonData = false;
+      } catch (error) {
+        console.error('插入商品信息失败:', error);
+        this.$message.error('插入商品信息失败,请重试');
+      }
+    }
   },
 };
 </script>

+ 57 - 4
ui/src/views/system/document/create.vue

@@ -2125,7 +2125,36 @@ export default {
           _this.insertPager();
           break;
         case "attr":
-          this.insertProductAttr();
+          // 直接调用插入逻辑,不再检查 linkProduct
+          let com = _this.coms[_this.comIndex];
+          if (com && e.content) {
+            let data = {
+              type: "ProductAttr",
+              id: e.content.id,
+              dataId: "",
+              name: "商品属性",
+              intro: "商品属性",
+              content: e.content.content || "",
+              attrs: e.content.attrs
+            };
+            
+            com.attrs.push(data);
+            _this.insertCmd = {
+              content: "{{" + data.id + "}}"
+            };
+            
+            // 可选:将产品信息添加到文档关联商品
+            if (e.content.attrs.productInfo) {
+              const productInfo = e.content.attrs.productInfo;
+              if (!_this.docAttr.linkProduct) {
+                _this.docAttr.linkProduct = [];
+              }
+              // 避免重复添加
+              if (!_this.docAttr.linkProduct.some(p => p.dcp_p_no === productInfo.dcp_p_no)) {
+                _this.docAttr.linkProduct.push(productInfo);
+              }
+            }
+          }
           break;
         case "Directory": //插入主题
           _this.insertDirectory(e);
@@ -2183,7 +2212,7 @@ export default {
 
       let data = {
         type: "ai",
-        id: "ai" + (com.attrs.length + 1),
+        id: "ai" + (com.attrs.length + 1), 
         dataId: "",
         name: "AI",
         intro: "插入AI",
@@ -2199,10 +2228,19 @@ export default {
       }
     },
     insertProductAttr() {
-      if (this.docAttr.linkProduct.length <= 0) {
-        this.$alert("请选择文档关联商品信息");
+      let _this = this;
+      if (_this.comIndex < 0) {
+        _this.$alert("请选择插入图层");
         return false;
       }
+      
+      let com = _this.coms[_this.comIndex];
+      if (!com) {
+        _this.$alert("请先添加文档内容");
+        return false;
+      }
+
+      // 允许直接插入产品属性,移除原有的限制检查
       this.showProductAttr = true;
     },
     //插入目录
@@ -3041,6 +3079,21 @@ export default {
         await this.initCategoryList();
       }
     },
+
+    // 添加一个用于检查产品是否已关联的方法
+    isProductLinked(dcp_p_no) {
+      return this.docAttr.linkProduct && 
+             this.docAttr.linkProduct.some(p => p.dcp_p_no === dcp_p_no);
+    },
+
+    // 修改文档属性更新方法
+    updateDocAttr() {
+      // ... 其他代码保持不变 ...
+      if (this.docAttr.linkProduct && this.docAttr.linkProduct.length > 0) {
+        this.docAttr.links = JSON.stringify(this.docAttr.linkProduct);
+      }
+      // ... 其他代码保持不变 ...
+    }
   },
 };
 </script>