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/components/SpuTableSelect.vue |  303 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 303 insertions(+), 0 deletions(-)

diff --git a/src/views/mall/product/spu/components/SpuTableSelect.vue b/src/views/mall/product/spu/components/SpuTableSelect.vue
new file mode 100644
index 0000000..4775e11
--- /dev/null
+++ b/src/views/mall/product/spu/components/SpuTableSelect.vue
@@ -0,0 +1,303 @@
+<template>
+  <Dialog v-model="dialogVisible" :appendToBody="true" title="閫夋嫨鍟嗗搧" width="70%">
+    <ContentWrap>
+      <el-form
+        ref="queryFormRef"
+        :inline="true"
+        :model="queryParams"
+        class="-mb-15px"
+        label-width="68px"
+      >
+        <el-form-item label="鍟嗗搧鍚嶇О" prop="name">
+          <el-input
+            v-model="queryParams.name"
+            class="!w-240px"
+            clearable
+            placeholder="璇疯緭鍏ュ晢鍝佸悕绉�"
+            @keyup.enter="handleQuery"
+          />
+        </el-form-item>
+        <el-form-item label="鍟嗗搧鍒嗙被" prop="categoryId">
+          <el-tree-select
+            v-model="queryParams.categoryId"
+            :data="categoryTreeList"
+            :props="defaultProps"
+            check-strictly
+            class="!w-240px"
+            node-key="id"
+            placeholder="璇烽�夋嫨鍟嗗搧鍒嗙被"
+          />
+        </el-form-item>
+        <el-form-item label="鍒涘缓鏃堕棿" prop="createTime">
+          <el-date-picker
+            v-model="queryParams.createTime"
+            :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+            class="!w-240px"
+            end-placeholder="缁撴潫鏃ユ湡"
+            start-placeholder="寮�濮嬫棩鏈�"
+            type="daterange"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="handleQuery">
+            <Icon class="mr-5px" icon="ep:search" />
+            鎼滅储
+          </el-button>
+          <el-button @click="resetQuery">
+            <Icon class="mr-5px" icon="ep:refresh" />
+            閲嶇疆
+          </el-button>
+        </el-form-item>
+      </el-form>
+      <el-table v-loading="loading" :data="list" show-overflow-tooltip>
+        <!-- 1. 澶氶�夋ā寮忥紙涓嶈兘浣跨敤type="selection"锛孍lement浼氬拷鐣eader鎻掓Ы锛� -->
+        <el-table-column width="55" v-if="multiple">
+          <template #header>
+            <el-checkbox
+              v-model="isCheckAll"
+              :indeterminate="isIndeterminate"
+              @change="handleCheckAll"
+            />
+          </template>
+          <template #default="{ row }">
+            <el-checkbox
+              v-model="checkedStatus[row.id]"
+              @change="(checked: boolean) => handleCheckOne(checked, row, true)"
+            />
+          </template>
+        </el-table-column>
+        <!-- 2. 鍗曢�夋ā寮� -->
+        <el-table-column label="#" width="55" v-else>
+          <template #default="{ row }">
+            <el-radio :value="row.id" v-model="selectedSpuId" @change="handleSingleSelected(row)">
+              <!-- 绌烘牸涓嶈兘鐪佺暐锛屾槸涓轰簡璁╁崟閫夋涓嶆樉绀簂abel锛屽鏋滀笉鎸囧畾label涓嶄細鏈夐�変腑鐨勬晥鏋� -->
+              &nbsp;
+            </el-radio>
+          </template>
+        </el-table-column>
+        <el-table-column key="id" align="center" label="鍟嗗搧缂栧彿" prop="id" min-width="60" />
+        <el-table-column label="鍟嗗搧鍥�" min-width="80">
+          <template #default="{ row }">
+            <el-image
+              :src="row.picUrl"
+              class="h-30px w-30px"
+              :preview-src-list="[row.picUrl]"
+              preview-teleported
+            />
+          </template>
+        </el-table-column>
+        <el-table-column label="鍟嗗搧鍚嶇О" min-width="200" prop="name" />
+        <el-table-column label="鍟嗗搧鍒嗙被" min-width="100" prop="categoryId">
+          <template #default="{ row }">
+            <span>{{ categoryList?.find((c) => c.id === row.categoryId)?.name }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 鍒嗛〉 -->
+      <Pagination
+        v-model:limit="queryParams.pageSize"
+        v-model:page="queryParams.pageNo"
+        :total="total"
+        @pagination="getList"
+      />
+    </ContentWrap>
+    <template #footer v-if="multiple">
+      <el-button type="primary" @click="handleEmitChange">纭� 瀹�</el-button>
+      <el-button @click="dialogVisible = false">鍙� 娑�</el-button>
+    </template>
+  </Dialog>
+</template>
+
+<script lang="ts" setup>
+import { defaultProps, handleTree } from '@/utils/tree'
+
+import * as ProductCategoryApi from '@/api/mall/product/category'
+import * as ProductSpuApi from '@/api/mall/product/spu'
+import { propTypes } from '@/utils/propTypes'
+import { CHANGE_EVENT } from 'element-plus'
+
+type Spu = Required<ProductSpuApi.Spu>
+
+/**
+ * 鍟嗗搧琛ㄦ牸閫夋嫨瀵硅瘽妗�
+ * 1. 鍗曢�夋ā寮忥細
+ *    1.1 鐐瑰嚮琛ㄦ牸宸︿晶鐨勫崟閫夋鏃讹紝缁撴潫閫夋嫨锛屽苟鍏抽棴瀵硅瘽妗�
+ *    1.2 鍐嶆鎵撳紑鏃讹紝淇濇寔閫変腑鐘舵��
+ * 2. 澶氶�夋ā寮忥細
+ *    2.1 鐐瑰嚮琛ㄦ牸宸︿晶鐨勫閫夋鏃讹紝璁板綍閫変腑鐨勫晢鍝�
+ *    2.2 鍒囨崲鍒嗛〉鏃讹紝淇濇寔鍟嗗搧鐨勯�変腑鐨勭姸鎬�
+ *    2.3 鐐瑰嚮鍙充笅瑙掔殑纭畾鎸夐挳鏃讹紝缁撴潫閫夋嫨锛屽叧闂璇濇
+ *    2.4 鍐嶆鎵撳紑鏃讹紝淇濇寔閫変腑鐘舵��
+ */
+defineOptions({ name: 'SpuTableSelect' })
+
+defineProps({
+  // 澶氶�夋ā寮�
+  multiple: propTypes.bool.def(false)
+})
+
+// 鍒楄〃鐨勬�婚〉鏁�
+const total = ref(0)
+// 鍒楄〃鐨勬暟鎹�
+const list = ref<Spu[]>([])
+// 鍒楄〃鐨勫姞杞戒腑
+const loading = ref(false)
+// 寮圭獥鐨勬槸鍚﹀睍绀�
+const dialogVisible = ref(false)
+// 鏌ヨ鍙傛暟
+const queryParams = ref({
+  pageNo: 1,
+  pageSize: 10,
+  // 榛樿鑾峰彇涓婃灦鐨勫晢鍝�
+  tabType: 0,
+  name: '',
+  categoryId: null,
+  createTime: []
+})
+
+/** 鎵撳紑寮圭獥 */
+const open = (spuList?: Spu[]) => {
+  // 閲嶇疆
+  checkedSpus.value = []
+  checkedStatus.value = {}
+  isCheckAll.value = false
+  isIndeterminate.value = false
+
+  // 澶勭悊宸查�変腑
+  if (spuList && spuList.length > 0) {
+    checkedSpus.value = [...spuList]
+    checkedStatus.value = Object.fromEntries(spuList.map((spu) => [spu.id, true]))
+  }
+
+  dialogVisible.value = true
+  resetQuery()
+}
+// 鎻愪緵 open 鏂规硶锛岀敤浜庢墦寮�寮圭獥
+defineExpose({ open })
+
+/** 鏌ヨ鍒楄〃 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await ProductSpuApi.getSpuPage(queryParams.value)
+    list.value = data.list
+    total.value = data.total
+    // checkbox缁戝畾undefined浼氭湁闂锛岄渶瑕佺粰涓�涓猙ool鍊�
+    list.value.forEach(
+      (spu) => (checkedStatus.value[spu.id] = checkedStatus.value[spu.id] || false)
+    )
+    // 璁$畻鍏ㄩ�夋鐘舵��
+    calculateIsCheckAll()
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNo = 1
+  getList()
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryParams.value = {
+    pageNo: 1,
+    pageSize: 10,
+    // 榛樿鑾峰彇涓婃灦鐨勫晢鍝�
+    tabType: 0,
+    name: '',
+    categoryId: null,
+    createTime: []
+  }
+  getList()
+}
+
+// 鏄惁鍏ㄩ��
+const isCheckAll = ref(false)
+// 鍏ㄩ�夋鏄惁澶勪簬涓棿鐘舵�侊細涓嶆槸鍏ㄩ儴閫変腑 && 浠绘剰涓�涓�変腑
+const isIndeterminate = ref(false)
+// 閫変腑鐨勫晢鍝�
+const checkedSpus = ref<Spu[]>([])
+// 閫変腑鐘舵�侊細key涓哄晢鍝両D锛寁alue涓烘槸鍚﹂�変腑
+const checkedStatus = ref<Record<string, boolean>>({})
+
+// 閫変腑鐨勫晢鍝� spuId
+const selectedSpuId = ref()
+/** 鍗曢�変腑鏃惰Е鍙� */
+const handleSingleSelected = (spu: Spu) => {
+  emits(CHANGE_EVENT, spu)
+  // 鍏抽棴寮圭獥
+  dialogVisible.value = false
+  // 璁颁綇涓婃閫夋嫨鐨処D
+  selectedSpuId.value = spu.id
+}
+
+/** 澶氶�夊畬鎴� */
+const handleEmitChange = () => {
+  // 鍏抽棴寮圭獥
+  dialogVisible.value = false
+  emits(CHANGE_EVENT, [...checkedSpus.value])
+}
+
+/** 纭閫夋嫨鏃剁殑瑙﹀彂浜嬩欢 */
+const emits = defineEmits<{
+  change: [spu: Spu | Spu[] | any]
+}>()
+
+/** 鍏ㄩ��/鍏ㄤ笉閫� */
+const handleCheckAll = (checked: boolean) => {
+  isCheckAll.value = checked
+  isIndeterminate.value = false
+
+  list.value.forEach((spu) => handleCheckOne(checked, spu, false))
+}
+
+/**
+ * 閫変腑涓�琛�
+ * @param checked 鏄惁閫変腑
+ * @param spu 鍟嗗搧
+ * @param isCalcCheckAll 鏄惁璁$畻鍏ㄩ��
+ */
+const handleCheckOne = (checked: boolean, spu: Spu, isCalcCheckAll: boolean) => {
+  if (checked) {
+    checkedSpus.value.push(spu)
+    checkedStatus.value[spu.id] = true
+  } else {
+    const index = findCheckedIndex(spu)
+    if (index > -1) {
+      checkedSpus.value.splice(index, 1)
+      checkedStatus.value[spu.id] = false
+      isCheckAll.value = false
+    }
+  }
+
+  // 璁$畻鍏ㄩ�夋鐘舵��
+  if (isCalcCheckAll) {
+    calculateIsCheckAll()
+  }
+}
+
+// 鏌ユ壘鍟嗗搧鍦ㄥ凡閫変腑鍟嗗搧鍒楄〃涓殑绱㈠紩
+const findCheckedIndex = (spu: Spu) => checkedSpus.value.findIndex((item) => item.id === spu.id)
+
+// 璁$畻鍏ㄩ�夋鐘舵��
+const calculateIsCheckAll = () => {
+  isCheckAll.value = list.value.every((spu) => checkedStatus.value[spu.id])
+  // 璁$畻涓棿鐘舵�侊細涓嶆槸鍏ㄩ儴閫変腑 && 浠绘剰涓�涓�変腑
+  isIndeterminate.value = !isCheckAll.value && list.value.some((spu) => checkedStatus.value[spu.id])
+}
+
+// 鍒嗙被鍒楄〃
+const categoryList = ref()
+// 鍒嗙被鏍�
+const categoryTreeList = ref()
+/** 鍒濆鍖� **/
+onMounted(async () => {
+  await getList()
+  // 鑾峰緱鍒嗙被鏍�
+  categoryList.value = await ProductCategoryApi.getCategoryList({})
+  categoryTreeList.value = handleTree(categoryList.value, 'id', 'parentId')
+})
+</script>

--
Gitblit v1.8.0