From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/views/mall/product/spu/form/ProductAttributes.vue |  162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 162 insertions(+), 0 deletions(-)

diff --git a/src/views/mall/product/spu/form/ProductAttributes.vue b/src/views/mall/product/spu/form/ProductAttributes.vue
new file mode 100644
index 0000000..1bb24ff
--- /dev/null
+++ b/src/views/mall/product/spu/form/ProductAttributes.vue
@@ -0,0 +1,162 @@
+<!-- 鍟嗗搧鍙戝竷 - 搴撳瓨浠锋牸 - 灞炴�у垪琛� -->
+<template>
+  <el-col v-for="(item, index) in attributeList" :key="index">
+    <div>
+      <el-text class="mx-1">灞炴�у悕锛�</el-text>
+      <el-tag :closable="!isDetail" class="mx-1" type="success" @close="handleCloseProperty(index)">
+        {{ item.name }}
+      </el-tag>
+    </div>
+    <div>
+      <el-text class="mx-1">灞炴�у�硷細</el-text>
+      <el-tag
+        v-for="(value, valueIndex) in item.values"
+        :key="value.id"
+        :closable="!isDetail"
+        class="mx-1"
+        @close="handleCloseValue(index, valueIndex)"
+      >
+        {{ value.name }}
+      </el-tag>
+      <el-select
+        v-show="inputVisible(index)"
+        :id="`input${index}`"
+        :ref="setInputRef"
+        v-model="inputValue"
+        :reserve-keyword="false"
+        allow-create
+        class="!w-30"
+        default-first-option
+        filterable
+        size="small"
+        @blur="handleInputConfirm(index, item.id)"
+        @change="handleInputConfirm(index, item.id)"
+        @keyup.enter="handleInputConfirm(index, item.id)"
+      >
+        <el-option
+          v-for="item2 in attributeOptions"
+          :key="item2.id"
+          :label="item2.name"
+          :value="item2.name"
+        />
+      </el-select>
+      <el-button
+        v-show="!inputVisible(index)"
+        class="button-new-tag ml-1"
+        size="small"
+        @click="showInput(index)"
+      >
+        + 娣诲姞
+      </el-button>
+    </div>
+    <el-divider class="my-10px" />
+  </el-col>
+</template>
+
+<script lang="ts" setup>
+import * as PropertyApi from '@/api/mall/product/property'
+import { PropertyAndValues } from '@/views/mall/product/spu/components'
+import { propTypes } from '@/utils/propTypes'
+
+defineOptions({ name: 'ProductAttributes' })
+
+const { t } = useI18n() // 鍥介檯鍖�
+const message = useMessage() // 娑堟伅寮圭獥
+const inputValue = ref('') // 杈撳叆妗嗗��
+const attributeIndex = ref<number | null>(null) // 鑾峰彇鐒︾偣鏃惰褰曞綋鍓嶅睘鎬ч」鐨刬ndex
+// 杈撳叆妗嗘樉闅愭帶鍒�
+const inputVisible = computed(() => (index: number) => {
+  if (attributeIndex.value === null) return false
+  if (attributeIndex.value === index) return true
+})
+const inputRef = ref<any[]>([]) //鏍囩杈撳叆妗哛ef
+/** 瑙e喅 ref 鍦� v-for 涓殑鑾峰彇闂*/
+const setInputRef = (el: any) => {
+  if (el === null || typeof el === 'undefined') return
+  // 濡傛灉涓嶅瓨鍦� id 鐩稿悓鐨勫厓绱犳墠娣诲姞
+  if (!inputRef.value.some((item) => item.inputRef?.attributes.id === el.inputRef?.attributes.id)) {
+    inputRef.value.push(el)
+  }
+}
+const attributeList = ref<PropertyAndValues[]>([]) // 鍟嗗搧灞炴�у垪琛�
+const attributeOptions = ref([] as PropertyApi.PropertyValueVO[]) // 鍟嗗搧灞炴�у悕绉颁笅鎷夋
+const props = defineProps({
+  propertyList: {
+    type: Array,
+    default: () => {}
+  },
+  isDetail: propTypes.bool.def(false) // 鏄惁浣滀负璇︽儏缁勪欢
+})
+
+watch(
+  () => props.propertyList,
+  (data) => {
+    if (!data) return
+    attributeList.value = data as any
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+
+/** 鍒犻櫎灞炴�у��*/
+const handleCloseValue = (index: number, valueIndex: number) => {
+  attributeList.value[index].values?.splice(valueIndex, 1)
+}
+
+/** 鍒犻櫎灞炴��*/
+const handleCloseProperty = (index: number) => {
+  attributeList.value?.splice(index, 1)
+  emit('success', attributeList.value)
+}
+
+/** 鏄剧ず杈撳叆妗嗗苟鑾峰彇鐒︾偣 */
+const showInput = async (index: number) => {
+  attributeIndex.value = index
+  inputRef.value[index].focus()
+  // 鑾峰彇灞炴�т笅鎷夐�夐」
+  await getAttributeOptions(attributeList.value[index].id)
+}
+
+/** 杈撳叆妗嗗け鍘荤劍鐐规垨鐐瑰嚮鍥炶溅鏃惰Е鍙� */
+const emit = defineEmits(['success']) // 瀹氫箟 success 浜嬩欢锛岀敤浜庢搷浣滄垚鍔熷悗鐨勫洖璋�
+const handleInputConfirm = async (index: number, propertyId: number) => {
+  if (inputValue.value) {
+    // 1. 閲嶅娣诲姞鏍¢獙
+    if (attributeList.value[index].values.find((item) => item.name === inputValue.value)) {
+      message.warning('宸插瓨鍦ㄧ浉鍚屽睘鎬у�硷紝璇烽噸璇�')
+      attributeIndex.value = null
+      inputValue.value = ''
+      return
+    }
+
+    // 2.1 鎯呭喌涓�锛氬睘鎬у�煎凡瀛樺湪锛屽垯鐩存帴浣跨敤骞剁粨鏉�
+    const existValue = attributeOptions.value.find((item) => item.name === inputValue.value)
+    if (existValue) {
+      attributeIndex.value = null
+      inputValue.value = ''
+      attributeList.value[index].values.push({ id: existValue.id, name: existValue.name })
+      emit('success', attributeList.value)
+      return
+    }
+
+    // 2.2 鎯呭喌浜岋細鏂板睘鎬у�硷紝鍒欒繘琛屼繚瀛�
+    try {
+      const id = await PropertyApi.createPropertyValue({ propertyId, name: inputValue.value })
+      attributeList.value[index].values.push({ id, name: inputValue.value })
+      message.success(t('common.createSuccess'))
+      emit('success', attributeList.value)
+    } catch {
+      message.error('娣诲姞澶辫触锛岃閲嶈瘯')
+    }
+  }
+  attributeIndex.value = null
+  inputValue.value = ''
+}
+
+/** 鑾峰彇鍟嗗搧灞炴�т笅鎷夐�夐」 */
+const getAttributeOptions = async (propertyId: number) => {
+  attributeOptions.value = await PropertyApi.getPropertyValueSimpleList(propertyId)
+}
+</script>

--
Gitblit v1.8.0