|
@@ -0,0 +1,296 @@
|
|
|
|
+<template>
|
|
|
|
+ <div v-loading="containerLoading">
|
|
|
|
+ <el-scrollbar height="450px">
|
|
|
|
+ <el-tree :data="searchClassifyTableData" :props="defaultProps" :expand-on-click-node="false">
|
|
|
|
+ <template #default="{ node, data }">
|
|
|
|
+ <span class="custom-tree-node">
|
|
|
|
+ <span style="width: 75%">{{ node.label }}</span>
|
|
|
|
+ <span style="color: rgb(50, 108, 216)" v-if="data.ta == true">
|
|
|
|
+ <a @click="refine(data)"> 细化 </a>
|
|
|
|
+ <a style="margin-left: 8px" @click="orientate(node, data)"> 定向 </a>
|
|
|
|
+ </span>
|
|
|
|
+ </span>
|
|
|
|
+ </template>
|
|
|
|
+ </el-tree>
|
|
|
|
+ </el-scrollbar>
|
|
|
|
+ </div>
|
|
|
|
+ <el-dialog v-model="visible" :title="`细化分类: ${dialogTitle}`" @close="dialogClose" destroy-on-close>
|
|
|
|
+ <div style="display: flex; justify-content: space-between">
|
|
|
|
+ <span>根据特定品牌、价格范围、星级和Prime配送资格,细化分类</span>
|
|
|
|
+ <span>
|
|
|
|
+ <el-checkbox v-model="dialogForm.isCount" label="显示商品数量" @change="isCountChanged" />
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+ <el-form :model="dialogForm" :rules="dialogRules" ref="dialogFormRef" style="margin-top: 20px">
|
|
|
|
+ <el-form-item style="padding-left: 140px">
|
|
|
|
+ <span style="margin-right: 10px; color: #616266; font-weight: 500">品牌</span>
|
|
|
|
+ <el-select v-model="dialogForm.dialogselectValue" @change="dialogSelectChange" multiple placeholder="请选择" :loading="dialogSelectLoading">
|
|
|
|
+ <el-option v-for="item in dialogForm.dialogOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item prop="prices" style="padding-left: 112px; margin-top: 10px">
|
|
|
|
+ <span style="margin-right: 10px; color: #616266; font-weight: 500">价格范围</span>
|
|
|
|
+ <el-input-number v-model="dialogForm.prices.lowest" :min="1" :controls="false" placeholder="无最低商品价格" />
|
|
|
|
+ --
|
|
|
|
+ <el-input-number v-model="dialogForm.prices.highest" :min="1" :controls="false" placeholder="无最高商品价格" />
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item prop="starRating" style="padding-left: 85px; margin-top: 10px">
|
|
|
|
+ <span style="margin-right: 15px; color: #616266; font-weight: 500">查看星级评定</span>
|
|
|
|
+ <el-slider v-model="dialogForm.starRating" range show-stops :max="5" :marks="marks" style="width: 70%" />
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item prop="delivery" style="padding-left: 140px; margin-top: 30px">
|
|
|
|
+ <span style="margin-right: 10px; color: #616266; font-weight: 500">配送</span>
|
|
|
|
+ <el-radio-group v-model="dialogForm.delivery">
|
|
|
|
+ <el-radio label="all" style="font-weight: 400">所有</el-radio>
|
|
|
|
+ <el-radio label="eligible" style="font-weight: 400">具备Prime资格</el-radio>
|
|
|
|
+ <el-radio label="diseligible" style="font-weight: 400">不具备Prime资格</el-radio>
|
|
|
|
+ </el-radio-group>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-form>
|
|
|
|
+ <template #footer>
|
|
|
|
+ <div style="display: flex; justify-content: space-between">
|
|
|
|
+ <span v-loading="countLoadig">
|
|
|
|
+ 定位到的商品数量: <span v-if="dialogForm.isCount == true">{{ commodityCount[0]?.min }} - {{ commodityCount[0]?.max }}</span>
|
|
|
|
+ </span>
|
|
|
|
+ <span class="dialog-footer">
|
|
|
|
+ <el-button @click="visible = false">取消</el-button>
|
|
|
|
+ <el-button type="primary" @click="dialogFormSubmit">确定</el-button>
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ </el-dialog>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+import { onMounted, ref, watch, reactive, CSSProperties, defineEmits } from 'vue'
|
|
|
|
+import { request } from '/@/utils/service'
|
|
|
|
+import type { FormInstance, FormRules, TabsPaneContext } from 'element-plus'
|
|
|
|
+import { useShopInfo } from '/@/stores/shopInfo'
|
|
|
|
+import { storeToRefs } from 'pinia'
|
|
|
|
+import emitter from '/@/utils/emitter'
|
|
|
|
+
|
|
|
|
+const shopInfo = useShopInfo()
|
|
|
|
+const { profile } = storeToRefs(shopInfo)
|
|
|
|
+
|
|
|
|
+const containerLoading = ref(false)
|
|
|
|
+const categoryBiddingType = ref('customBid')
|
|
|
|
+const countLoadig = ref(false)
|
|
|
|
+const dialogSelectLoading = ref(false)
|
|
|
|
+const searchClassifyTableData = ref([])
|
|
|
|
+let productOrientationTableData = ref([])
|
|
|
|
+const defaultProps = {
|
|
|
|
+ children: 'ch',
|
|
|
|
+ label: 'cna',
|
|
|
|
+}
|
|
|
|
+const visible = ref(false)
|
|
|
|
+let dialogTitle = ref('')
|
|
|
|
+let categoryId = ref('')
|
|
|
|
+let commodityCount = ref([])
|
|
|
|
+let currentDialogIndex = ref(0)
|
|
|
|
+let selectedLabels = ref([]) // 选中的label数组
|
|
|
|
+
|
|
|
|
+interface Mark {
|
|
|
|
+ style: CSSProperties
|
|
|
|
+ label: string
|
|
|
|
+}
|
|
|
|
+type Marks = Record<number, Mark | string>
|
|
|
|
+const marks = reactive<Marks>({
|
|
|
|
+ 0: '0',
|
|
|
|
+ 1: '1',
|
|
|
|
+ 2: '2',
|
|
|
|
+ 3: '3',
|
|
|
|
+ 4: '4',
|
|
|
|
+ 5: '5',
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const dialogForm: any = reactive({
|
|
|
|
+ prices: {
|
|
|
|
+ lowest: undefined,
|
|
|
|
+ highest: undefined,
|
|
|
|
+ },
|
|
|
|
+ starRating: [0, 5],
|
|
|
|
+ dialogselectValue: [],
|
|
|
|
+ delivery: 'all',
|
|
|
|
+ isCount: false,
|
|
|
|
+})
|
|
|
|
+const dialogFormRef = ref()
|
|
|
|
+const dialogRules = reactive({
|
|
|
|
+ prices: [{ validator: validatePrices, trigger: 'blur' }],
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+async function validatePrices(rule, value) {
|
|
|
|
+ if (value.highest !== '' && value.lowest !== '' && value.highest <= value.lowest) {
|
|
|
|
+ return Promise.reject('最高价格必须大于最低价格')
|
|
|
|
+ }
|
|
|
|
+ return Promise.resolve()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async function setProductOrientationData() {
|
|
|
|
+ containerLoading.value = true
|
|
|
|
+ try {
|
|
|
|
+ const resp = await request({
|
|
|
|
+ url: '/api/ad_manage/targetable/categories/',
|
|
|
|
+ method: 'GET',
|
|
|
|
+ params: {
|
|
|
|
+ profile_id: profile.value.profile_id,
|
|
|
|
+ },
|
|
|
|
+ })
|
|
|
|
+ searchClassifyTableData.value = resp.data
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('请求失败:', error)
|
|
|
|
+ } finally {
|
|
|
|
+ containerLoading.value = false
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function dialogSelectChange(event) {
|
|
|
|
+ // 使用 map 来转换每个选中项的 value 为其对应的 label
|
|
|
|
+ selectedLabels.value = event.map((selectedValue) => {
|
|
|
|
+ const selectedOption = dialogForm.dialogOptions.find((option) => option.value === selectedValue)
|
|
|
|
+ return selectedOption ? selectedOption.label : ''
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function resetDialogForm() {
|
|
|
|
+ dialogForm.prices.lowest = undefined
|
|
|
|
+ dialogForm.prices.highest = undefined
|
|
|
|
+ dialogForm.starRating = [0, 5]
|
|
|
|
+ dialogForm.dialogselectValue = []
|
|
|
|
+ dialogForm.delivery = 'all'
|
|
|
|
+ dialogForm.isCount = false
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function dialogClose() {
|
|
|
|
+ currentDialogIndex.value++
|
|
|
|
+ resetDialogForm()
|
|
|
|
+ dialogForm.isCount = false
|
|
|
|
+ commodityCount.value = []
|
|
|
|
+ countLoadig.value = false
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async function getCount(instanceId) {
|
|
|
|
+ try {
|
|
|
|
+ const resp = await request({
|
|
|
|
+ url: '/api/ad_manage/products/count/',
|
|
|
|
+ method: 'POST',
|
|
|
|
+ data: {
|
|
|
|
+ profile_id: profile.value.profile_id,
|
|
|
|
+ category_id: categoryId.value,
|
|
|
|
+ },
|
|
|
|
+ })
|
|
|
|
+ if (instanceId === currentDialogIndex.value) {
|
|
|
|
+ commodityCount.value = resp.data.AsinCounts
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('请求失败:', error)
|
|
|
|
+ } finally {
|
|
|
|
+ if (instanceId === currentDialogIndex.value) {
|
|
|
|
+ countLoadig.value = false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function isCountChanged() {
|
|
|
|
+ if (dialogForm.isCount) {
|
|
|
|
+ const instanceId = currentDialogIndex.value
|
|
|
|
+ countLoadig.value = true
|
|
|
|
+ getCount(instanceId)
|
|
|
|
+ } else {
|
|
|
|
+ countLoadig.value = false
|
|
|
|
+ commodityCount.value = []
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async function setDialogOption() {
|
|
|
|
+ try {
|
|
|
|
+ const resp = await request({
|
|
|
|
+ url: '/api/ad_manage/categories/brands/',
|
|
|
|
+ method: 'GET',
|
|
|
|
+ params: {
|
|
|
|
+ profile_id: profile.value.profile_id,
|
|
|
|
+ category_id: categoryId.value,
|
|
|
|
+ },
|
|
|
|
+ })
|
|
|
|
+ const options = resp.data
|
|
|
|
+ dialogForm.dialogOptions = options.brands.map((brand) => {
|
|
|
|
+ return {
|
|
|
|
+ label: brand.name,
|
|
|
|
+ value: brand.id,
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ dialogSelectLoading.value = false
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('请求失败:', error)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 细化按钮功能
|
|
|
|
+let refineItem = ref([])
|
|
|
|
+function refine(data) {
|
|
|
|
+ commodityCount.value = []
|
|
|
|
+ dialogTitle.value = data.cna
|
|
|
|
+ categoryId.value = data.cid
|
|
|
|
+ refineItem.value.push(data)
|
|
|
|
+ visible.value = true
|
|
|
|
+ dialogSelectLoading.value = true
|
|
|
|
+ setDialogOption()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const emit = defineEmits(['add-to-table','form-submitted'])
|
|
|
|
+
|
|
|
|
+// 定向按钮功能
|
|
|
|
+function orientate(node, data) {
|
|
|
|
+ productOrientationTableData.value.some((item) => item.cid === data.cid)
|
|
|
|
+ emit('add-to-table', data)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 弹窗确定按钮功能
|
|
|
|
+async function dialogFormSubmit() {
|
|
|
|
+ // 验证表单数据
|
|
|
|
+ const isValid = await dialogFormRef.value.validate()
|
|
|
|
+ if (!isValid) {
|
|
|
|
+ console.log('表单数据验证失败')
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 封装数据
|
|
|
|
+ const formData = {
|
|
|
|
+ dialogTitle: dialogTitle.value,
|
|
|
|
+ cid: categoryId.value,
|
|
|
|
+ prices: {
|
|
|
|
+ lowest: dialogForm.prices.lowest,
|
|
|
|
+ highest: dialogForm.prices.highest,
|
|
|
|
+ },
|
|
|
|
+ starRating: dialogForm.starRating,
|
|
|
|
+ selectedBrands: dialogForm.dialogselectValue,
|
|
|
|
+ delivery: dialogForm.delivery,
|
|
|
|
+ isCount: dialogForm.isCount,
|
|
|
|
+ commodityCount: commodityCount.value,
|
|
|
|
+ selectedLabels: selectedLabels.value, // 如果需要选中的标签也发送到父组件
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 使用emit发送封装好的数据到父组件
|
|
|
|
+ emit('form-submitted', formData);
|
|
|
|
+ // emitter.emit('form-submitted', formData)
|
|
|
|
+
|
|
|
|
+ // 关闭弹窗
|
|
|
|
+ visible.value = false
|
|
|
|
+ resetDialogForm()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+onMounted(() => {
|
|
|
|
+ setProductOrientationData()
|
|
|
|
+})
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style scoped>
|
|
|
|
+.custom-tree-node {
|
|
|
|
+ /* el-tree自定义样式 */
|
|
|
|
+ flex: 1;
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: space-between;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ padding-right: 8px;
|
|
|
|
+}
|
|
|
|
+</style>
|