|
@@ -1,61 +1,12 @@
|
|
<template>
|
|
<template>
|
|
<el-container class="goods-select">
|
|
<el-container class="goods-select">
|
|
- <el-aside class="select-aside">
|
|
|
|
- <el-select
|
|
|
|
- class="sa-m-b-16"
|
|
|
|
- v-model="categoryLevel"
|
|
|
|
- placeholder="Select"
|
|
|
|
- @change="getCategoryDetail"
|
|
|
|
- >
|
|
|
|
- <el-option
|
|
|
|
- v-for="item in category.data"
|
|
|
|
- :key="item.id"
|
|
|
|
- :label="item.name"
|
|
|
|
- :value="item.id"
|
|
|
|
- />
|
|
|
|
- </el-select>
|
|
|
|
- <el-tree
|
|
|
|
- :data="category.detail"
|
|
|
|
- empty-text=""
|
|
|
|
- :props="{
|
|
|
|
- children: 'children',
|
|
|
|
- label: 'name',
|
|
|
|
- }"
|
|
|
|
- :indent="0"
|
|
|
|
- :accordion="true"
|
|
|
|
- >
|
|
|
|
- <template #default="{ node, data }">
|
|
|
|
- <span
|
|
|
|
- class="custom-tree-node sa-flex"
|
|
|
|
- :class="data.id == searchData.category_ids ? 'is-active' : ''"
|
|
|
|
- :style="{ 'padding-left': node.level * 16 + 'px' }"
|
|
|
|
- @click.stop="changeCategoryIds(data)"
|
|
|
|
- >
|
|
|
|
- <div class="sa-table-line-1">{{ data.name }}</div>
|
|
|
|
- <el-icon
|
|
|
|
- :class="['sa-m-l-8', node.expanded ? 'expand-arrow-up' : 'expand-arrow-down']"
|
|
|
|
- v-if="data.children && data.children.length"
|
|
|
|
- @click.stop="node.expanded = !node.expanded"
|
|
|
|
- >
|
|
|
|
- <ArrowDown />
|
|
|
|
- </el-icon>
|
|
|
|
- </span>
|
|
|
|
- </template>
|
|
|
|
- </el-tree>
|
|
|
|
- </el-aside>
|
|
|
|
<el-container>
|
|
<el-container>
|
|
- <el-header class="goods-search sa-flex sa-flex-wrap sa-row-between">
|
|
|
|
- <div class="sa-flex sa-flex-wrap goods-price">
|
|
|
|
- <span class="sa-m-r-12">商品价格</span>
|
|
|
|
- <el-input class="sa-w-88" v-model="searchData.price.min" placeholder="最低价格" />
|
|
|
|
- <span class="sa-m-l-12 sa-m-r-12">至</span>
|
|
|
|
- <el-input class="sa-w-88" v-model="searchData.price.max" placeholder="最高价格" />
|
|
|
|
- </div>
|
|
|
|
- <el-input
|
|
|
|
- class="sa-w-180"
|
|
|
|
- v-model="searchData.keyword"
|
|
|
|
- prefix-icon="Search"
|
|
|
|
- placeholder="请输入搜索内容"
|
|
|
|
|
|
+ <el-header class="goods-search">
|
|
|
|
+ <sa-search-simple
|
|
|
|
+ :searchFields="searchFields"
|
|
|
|
+ :defaultValues="defaultSearchValues"
|
|
|
|
+ @search="(val) => getData(1, val)"
|
|
|
|
+ @reset="getData(1)"
|
|
/>
|
|
/>
|
|
</el-header>
|
|
</el-header>
|
|
<el-main v-loading="loading">
|
|
<el-main v-loading="loading">
|
|
@@ -75,37 +26,54 @@
|
|
type="selection"
|
|
type="selection"
|
|
width="48"
|
|
width="48"
|
|
></el-table-column>
|
|
></el-table-column>
|
|
- <el-table-column prop="id" label="ID" min-width="88" />
|
|
|
|
- <el-table-column label="商品信息" min-width="310">
|
|
|
|
- <template #default="scoped">
|
|
|
|
- <div class="sa-flex">
|
|
|
|
- <sa-image :url="scoped.row.image" size="40"></sa-image>
|
|
|
|
- <div class="sa-m-l-8">
|
|
|
|
- <div class="title sa-m-b-6 sa-table-line-1">
|
|
|
|
- {{ scoped.row.title }}
|
|
|
|
- </div>
|
|
|
|
- <div class="price">¥{{ scoped.row.price.join('~¥') }}</div>
|
|
|
|
|
|
+ <el-table-column prop="id" label="商品编号" min-width="120" align="center" />
|
|
|
|
+ <el-table-column label="商品信息" min-width="300">
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ <div class="sa-flex sa-row-center">
|
|
|
|
+ <el-image
|
|
|
|
+ :src="scope.row.image"
|
|
|
|
+ style="width: 60px; height: 60px; margin-right: 12px"
|
|
|
|
+ fit="cover"
|
|
|
|
+ />
|
|
|
|
+ <div>
|
|
|
|
+ <div class="goods-title">{{ scope.row.title }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table-column>
|
|
- <el-table-column prop="stock" label="库存" min-width="120"></el-table-column>
|
|
|
|
|
|
+ <el-table-column label="商品价格" min-width="120" align="center">
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ <div v-if="scope.row.price && Array.isArray(scope.row.price)">
|
|
|
|
+ ৳{{ scope.row.price[0]?.replace(/[¥¥]/g, '').replace(/,/g, '') || '0' }}
|
|
|
|
+ </div>
|
|
|
|
+ <div v-else>
|
|
|
|
+ ৳{{
|
|
|
|
+ scope.row.current_price?.replace(/[¥¥]/g, '').replace(/,/g, '') ||
|
|
|
|
+ scope.row.original_price?.replace(/[¥¥]/g, '').replace(/,/g, '') ||
|
|
|
|
+ '0'
|
|
|
|
+ }}
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="stock" label="剩余库存" min-width="100" align="center">
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ {{ scope.row.stock || 0 }}
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
<el-table-column v-if="!modal.params.multiple" label="操作" width="80">
|
|
<el-table-column v-if="!modal.params.multiple" label="操作" width="80">
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
<template v-if="modal.params.ftype == 'score_shop'">
|
|
<template v-if="modal.params.ftype == 'score_shop'">
|
|
<span v-if="scope.row.is_score_shop">已参加</span>
|
|
<span v-if="scope.row.is_score_shop">已参加</span>
|
|
<el-button
|
|
<el-button
|
|
v-if="!scope.row.is_score_shop"
|
|
v-if="!scope.row.is_score_shop"
|
|
- class="is-link"
|
|
|
|
|
|
+ link
|
|
type="primary"
|
|
type="primary"
|
|
@click="singleSelect(scope.row.id)"
|
|
@click="singleSelect(scope.row.id)"
|
|
>参加</el-button
|
|
>参加</el-button
|
|
>
|
|
>
|
|
</template>
|
|
</template>
|
|
<template v-else>
|
|
<template v-else>
|
|
- <el-button class="is-link" type="primary" @click="singleSelect(scope.row.id)"
|
|
|
|
- >选择</el-button
|
|
|
|
- >
|
|
|
|
|
|
+ <el-button link type="primary" @click="singleSelect(scope.row.id)">选择</el-button>
|
|
</template>
|
|
</template>
|
|
</template>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table-column>
|
|
@@ -134,121 +102,76 @@
|
|
* @param {Number} max - 多选时最大数量(0 代表限制数量)
|
|
* @param {Number} max - 多选时最大数量(0 代表限制数量)
|
|
* @param {String} ftype - 打开来源(score_shop)
|
|
* @param {String} ftype - 打开来源(score_shop)
|
|
*/
|
|
*/
|
|
- import { nextTick, onMounted, reactive, ref, watch } from 'vue';
|
|
|
|
|
|
+ import { nextTick, onMounted, reactive, ref } from 'vue';
|
|
import { api } from '../goods.service';
|
|
import { api } from '../goods.service';
|
|
- import { api as categoryApi } from '@/app/shop/admin/goods/category/category.service';
|
|
|
|
import { ElMessage } from 'element-plus';
|
|
import { ElMessage } from 'element-plus';
|
|
- import { composeFilter } from '@/sheep/utils';
|
|
|
|
- import { useDebounceFn } from '@vueuse/core';
|
|
|
|
import { usePagination } from '@/sheep/hooks';
|
|
import { usePagination } from '@/sheep/hooks';
|
|
- import { cloneDeep } from 'lodash';
|
|
|
|
|
|
|
|
const { pageData } = usePagination();
|
|
const { pageData } = usePagination();
|
|
|
|
|
|
const emit = defineEmits(['modalCallBack']);
|
|
const emit = defineEmits(['modalCallBack']);
|
|
const props = defineProps(['modal']);
|
|
const props = defineProps(['modal']);
|
|
|
|
|
|
- // 分类
|
|
|
|
- const categoryLevel = ref('');
|
|
|
|
- const category = reactive({
|
|
|
|
- data: [],
|
|
|
|
- detail: [],
|
|
|
|
|
|
+ // 搜索字段配置
|
|
|
|
+ const searchFields = reactive({
|
|
|
|
+ title: {
|
|
|
|
+ type: 'input',
|
|
|
|
+ label: '商品名称',
|
|
|
|
+ placeholder: '请输入名称或编号',
|
|
|
|
+ width: 200,
|
|
|
|
+ },
|
|
});
|
|
});
|
|
|
|
|
|
- async function getCategory() {
|
|
|
|
- const { data } = await categoryApi.select();
|
|
|
|
- category.data = data;
|
|
|
|
- category.data.unshift({
|
|
|
|
- children: [],
|
|
|
|
- id: 'all',
|
|
|
|
- name: '全部分类',
|
|
|
|
- });
|
|
|
|
- if (data.length > 0) {
|
|
|
|
- categoryLevel.value = data[0].id;
|
|
|
|
- getCategoryDetail();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- async function getCategoryDetail() {
|
|
|
|
- const data = category.data.find((item) => item.id == categoryLevel.value);
|
|
|
|
- category.detail = data.children || [];
|
|
|
|
- searchData.category_ids = data.id;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function changeCategoryIds(data) {
|
|
|
|
- searchData.category_ids = data.id;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const searchData = reactive({
|
|
|
|
- category_ids: '',
|
|
|
|
- keyword: '',
|
|
|
|
- price: {
|
|
|
|
- min: '',
|
|
|
|
- max: '',
|
|
|
|
- },
|
|
|
|
|
|
+ // 默认搜索值
|
|
|
|
+ const defaultSearchValues = reactive({
|
|
|
|
+ title: '',
|
|
});
|
|
});
|
|
|
|
|
|
const loading = ref(true);
|
|
const loading = ref(true);
|
|
const table = reactive({
|
|
const table = reactive({
|
|
list: [],
|
|
list: [],
|
|
- // selected: [],
|
|
|
|
- ids: props.modal.params.ids,
|
|
|
|
|
|
+ ids: props.modal.params.ids || [],
|
|
|
|
+ selectedGoods: [], // 存储选中的商品完整数据
|
|
});
|
|
});
|
|
|
|
|
|
- async function getData() {
|
|
|
|
|
|
+ async function getData(page, searchParams = {}) {
|
|
|
|
+ if (page) pageData.page = page;
|
|
loading.value = true;
|
|
loading.value = true;
|
|
- let tempSearch = cloneDeep(searchData);
|
|
|
|
- tempSearch.category_ids = tempSearch.category_ids + '';
|
|
|
|
- if (tempSearch.price.min && tempSearch.price.max) {
|
|
|
|
- tempSearch.price = `${tempSearch.price.min} - ${tempSearch.price.max}`;
|
|
|
|
- }
|
|
|
|
- // activity_type 搜索
|
|
|
|
- if (props.modal.params.activity_type) {
|
|
|
|
- tempSearch.activity_type = props.modal.params.activity_type;
|
|
|
|
- }
|
|
|
|
- // id 搜索
|
|
|
|
- if (props.modal.params.goods_ids) {
|
|
|
|
- tempSearch.goods = { field: 'id', value: props.modal.params.goods_ids };
|
|
|
|
- }
|
|
|
|
|
|
|
|
- let search = composeFilter(tempSearch, {
|
|
|
|
- keyword: '=',
|
|
|
|
- price: 'between',
|
|
|
|
- });
|
|
|
|
- const { data } = await api.goods.select({
|
|
|
|
- page: pageData.page,
|
|
|
|
- list_rows: pageData.list_rows,
|
|
|
|
- ...search,
|
|
|
|
- data_type: props.modal.params.ftype || '',
|
|
|
|
- });
|
|
|
|
- table.list = data.data;
|
|
|
|
- pageData.page = data.current_page;
|
|
|
|
- pageData.list_rows = data.per_page;
|
|
|
|
- pageData.total = data.total;
|
|
|
|
|
|
+ try {
|
|
|
|
+ const params = {
|
|
|
|
+ page: pageData.page,
|
|
|
|
+ list_rows: pageData.list_rows,
|
|
|
|
+ ...searchParams,
|
|
|
|
+ };
|
|
|
|
|
|
- nextTick(() => {
|
|
|
|
- table.list.forEach((l) => {
|
|
|
|
- if (table.ids?.includes(l.id)) {
|
|
|
|
- multipleTableRef.value?.toggleRowSelection(l, true);
|
|
|
|
- toggleRowSelection('row', [l], l);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- });
|
|
|
|
|
|
+ const { error, data } = await api.goods.list(params);
|
|
|
|
+ if (error === 0) {
|
|
|
|
+ table.list = data.data;
|
|
|
|
+ pageData.page = data.current_page;
|
|
|
|
+ pageData.list_rows = data.per_page;
|
|
|
|
+ pageData.total = data.total;
|
|
|
|
|
|
- loading.value = false;
|
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
+ setDefaultSelected();
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('获取商品列表失败:', error);
|
|
|
|
+ } finally {
|
|
|
|
+ loading.value = false;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- const debouncedFn = useDebounceFn(getData, 500);
|
|
|
|
- watch(
|
|
|
|
- () => searchData,
|
|
|
|
- () => {
|
|
|
|
- pageData.page = 1;
|
|
|
|
- debouncedFn();
|
|
|
|
- },
|
|
|
|
- {
|
|
|
|
- deep: true,
|
|
|
|
- },
|
|
|
|
- );
|
|
|
|
|
|
+ // 设置默认选中
|
|
|
|
+ function setDefaultSelected() {
|
|
|
|
+ table.list.forEach((item) => {
|
|
|
|
+ if (table.ids?.includes(item.id)) {
|
|
|
|
+ multipleTableRef.value?.toggleRowSelection(item, true);
|
|
|
|
+ toggleRowSelection('row', [item], item);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
|
|
const multipleTableRef = ref();
|
|
const multipleTableRef = ref();
|
|
function selectRow(selection, row) {
|
|
function selectRow(selection, row) {
|
|
@@ -264,6 +187,8 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
toggleRowSelection('row', selection, row);
|
|
toggleRowSelection('row', selection, row);
|
|
|
|
+ // 更新选中的商品数据
|
|
|
|
+ updateSelectedGoods();
|
|
}
|
|
}
|
|
function selectAll(selection) {
|
|
function selectAll(selection) {
|
|
if (
|
|
if (
|
|
@@ -286,6 +211,8 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
toggleRowSelection('all', selection);
|
|
toggleRowSelection('all', selection);
|
|
|
|
+ // 更新选中的商品数据
|
|
|
|
+ updateSelectedGoods();
|
|
}
|
|
}
|
|
|
|
|
|
function toggleRowSelection(type, selection, row) {
|
|
function toggleRowSelection(type, selection, row) {
|
|
@@ -309,71 +236,76 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- async function singleSelect(id) {
|
|
|
|
- await getGoodsList(id);
|
|
|
|
- emit('modalCallBack', {
|
|
|
|
- event: 'confirm',
|
|
|
|
- data: tempGoods.list[0],
|
|
|
|
- });
|
|
|
|
|
|
+ // 更新选中的商品数据
|
|
|
|
+ function updateSelectedGoods() {
|
|
|
|
+ table.selectedGoods = table.list
|
|
|
|
+ .filter((item) => table.ids.includes(item.id))
|
|
|
|
+ .map((item) => ({
|
|
|
|
+ id: item.id,
|
|
|
|
+ title: item.title,
|
|
|
|
+ image: item.image,
|
|
|
|
+ price: item.price || item.current_price || item.original_price,
|
|
|
|
+ stock: item.stock,
|
|
|
|
+ }));
|
|
}
|
|
}
|
|
|
|
|
|
- async function confirm() {
|
|
|
|
- await getGoodsList(table.ids.join(','));
|
|
|
|
- emit('modalCallBack', {
|
|
|
|
- event: 'confirm',
|
|
|
|
- data: tempGoods.list,
|
|
|
|
- });
|
|
|
|
|
|
+ function singleSelect(id) {
|
|
|
|
+ // 找到对应的商品数据
|
|
|
|
+ const selectedItem = table.list.find((item) => item.id === id);
|
|
|
|
+ if (selectedItem) {
|
|
|
|
+ const goodsData = {
|
|
|
|
+ id: selectedItem.id,
|
|
|
|
+ title: selectedItem.title,
|
|
|
|
+ image: selectedItem.image,
|
|
|
|
+ price: selectedItem.price || selectedItem.current_price || selectedItem.original_price,
|
|
|
|
+ stock: selectedItem.stock,
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ emit('modalCallBack', {
|
|
|
|
+ event: 'confirm',
|
|
|
|
+ data: goodsData,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- const tempGoods = reactive({
|
|
|
|
- list: [],
|
|
|
|
- });
|
|
|
|
|
|
+ function confirm() {
|
|
|
|
+ // 直接使用选中的商品数据
|
|
|
|
+ const selectedGoodsData =
|
|
|
|
+ table.selectedGoods.length > 0
|
|
|
|
+ ? table.selectedGoods
|
|
|
|
+ : table.list
|
|
|
|
+ .filter((item) => table.ids.includes(item.id))
|
|
|
|
+ .map((item) => ({
|
|
|
|
+ id: item.id,
|
|
|
|
+ title: item.title,
|
|
|
|
+ image: item.image,
|
|
|
|
+ price: item.price || item.current_price || item.original_price,
|
|
|
|
+ stock: item.stock,
|
|
|
|
+ }));
|
|
|
|
|
|
- async function getGoodsList(ids) {
|
|
|
|
- const { data } = await api.goods.select(
|
|
|
|
- {
|
|
|
|
- search: JSON.stringify({ id: [ids, 'in'] }),
|
|
|
|
- },
|
|
|
|
- 'select',
|
|
|
|
- );
|
|
|
|
- tempGoods.list = data;
|
|
|
|
|
|
+ emit('modalCallBack', {
|
|
|
|
+ event: 'confirm',
|
|
|
|
+ data: selectedGoodsData,
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
- getCategory();
|
|
|
|
|
|
+ getData();
|
|
});
|
|
});
|
|
</script>
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
.goods-select {
|
|
.goods-select {
|
|
- .select-aside {
|
|
|
|
- --el-aside-width: 180px;
|
|
|
|
- border-right: 1px solid var(--sa-border);
|
|
|
|
- padding: 20px;
|
|
|
|
- :deep() {
|
|
|
|
- .el-tree-node {
|
|
|
|
- .el-tree-node__content {
|
|
|
|
- height: auto;
|
|
|
|
- .el-tree-node__expand-icon {
|
|
|
|
- display: none;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- .custom-tree-node {
|
|
|
|
- width: 100%;
|
|
|
|
- height: 32px;
|
|
|
|
- line-height: 32px;
|
|
|
|
- border-radius: 4px;
|
|
|
|
- &.is-active {
|
|
|
|
- background: var(--t-bg-active);
|
|
|
|
- color: var(--el-color-primary);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
.goods-search {
|
|
.goods-search {
|
|
--el-header-height: auto;
|
|
--el-header-height: auto;
|
|
padding-top: var(--sa-padding);
|
|
padding-top: var(--sa-padding);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ .sa-footer--submit {
|
|
|
|
+ height: auto;
|
|
|
|
+ padding: 16px;
|
|
|
|
+ border-top: 1px solid var(--sa-border);
|
|
|
|
+ background: #fff;
|
|
|
|
+ }
|
|
.title {
|
|
.title {
|
|
height: 16px;
|
|
height: 16px;
|
|
line-height: 16px;
|
|
line-height: 16px;
|
|
@@ -387,6 +319,19 @@
|
|
font-weight: 400;
|
|
font-weight: 400;
|
|
color: #ff4d4f;
|
|
color: #ff4d4f;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ .goods-title {
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ color: #333;
|
|
|
|
+ line-height: 1.4;
|
|
|
|
+ word-break: break-word;
|
|
|
|
+ display: -webkit-box;
|
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
|
+ -webkit-line-clamp: 2;
|
|
|
|
+ line-clamp: 2;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
+ }
|
|
.sa-footer--submit {
|
|
.sa-footer--submit {
|
|
display: flex;
|
|
display: flex;
|
|
align-items: center;
|
|
align-items: center;
|