优化店铺商品及分类管理

This commit is contained in:
2024-07-27 23:07:06 +08:00
parent b9df13a916
commit d74b844ee0
12 changed files with 677 additions and 82 deletions

View File

@@ -1,7 +1,7 @@
VITE_APP_NAME=后台管理系统 VITE_APP_NAME=后台管理系统
VITE_SOCKET_URL=wss://server.gxwebsoft.com VITE_SOCKET_URL=wss://server.gxwebsoft.com
VITE_SERVER_URL=https://server.gxwebsoft.com/api VITE_SERVER_URL=https://server.gxwebsoft.com/api
VITE_API_URL=https://modules.gxwebsoft.com/api #VITE_API_URL=https://modules.gxwebsoft.com/api
#VITE_SERVER_URL=http://127.0.0.1:9091/api #VITE_SERVER_URL=http://127.0.0.1:9091/api
#VITE_API_URL=http://127.0.0.1:9099/api VITE_API_URL=http://127.0.0.1:9099/api

View File

@@ -52,6 +52,8 @@ export interface GoodsCategory {
label?: string; label?: string;
// 子菜单 // 子菜单
children?: GoodsCategory[]; children?: GoodsCategory[];
// 商铺ID
merchantId?: number;
} }
/** /**

View File

@@ -12,11 +12,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, watch } from 'vue'; import { ref, watch, reactive } from 'vue';
import type { ValueType } from 'ant-design-vue/es/vc-cascader/Cascader'; import type { ValueType } from 'ant-design-vue/es/vc-cascader/Cascader';
import { listGoodsCategory } from '@/api/shop/goodsCategory'; import { listGoodsCategory } from '@/api/shop/goodsCategory';
import { toTreeData } from 'ele-admin-pro/es'; import { toTreeData } from 'ele-admin-pro/es';
import { GoodsCategory } from '@/api/shop/goodsCategory/model'; import { GoodsCategory } from '@/api/shop/goodsCategory/model';
import { FileRecordParam } from '@/api/system/file/model';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -26,6 +27,7 @@
valueField?: 'label'; valueField?: 'label';
type?: 'provinceCity' | 'province'; type?: 'provinceCity' | 'province';
showSearch?: boolean; showSearch?: boolean;
merchantId?: number;
}>(), }>(),
{ {
showSearch: true showSearch: true
@@ -38,6 +40,11 @@
(e: 'load-data-done', value: GoodsCategory[]): void; (e: 'load-data-done', value: GoodsCategory[]): void;
}>(); }>();
// 搜索表单
const where = reactive<GoodsCategory>({
merchantId: undefined
});
// 级联选择器数据 // 级联选择器数据
const regionsData = ref<GoodsCategory[]>([]); const regionsData = ref<GoodsCategory[]>([]);
@@ -47,7 +54,7 @@
}; };
const onChange = (item: any, value: ValueType) => { const onChange = (item: any, value: ValueType) => {
console.log(item,value); console.log(item, value);
emit('done', item, value); emit('done', item, value);
}; };
@@ -107,7 +114,10 @@
watch( watch(
() => props.options, () => props.options,
() => { () => {
listGoodsCategory().then((data) => { if (props.merchantId) {
where.merchantId = props.merchantId;
}
listGoodsCategory(where).then((data) => {
const list = toTreeData({ const list = toTreeData({
data: data?.map((d) => { data: data?.map((d) => {
d.value = d.categoryId; d.value = d.categoryId;

View File

@@ -45,6 +45,7 @@
</div> </div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
v-if="!merchantId"
label="选择店铺" label="选择店铺"
name="merchantId" name="merchantId"
> >
@@ -64,7 +65,7 @@
v-model:value="form.goodsName" v-model:value="form.goodsName"
/> />
</a-form-item> </a-form-item>
<a-form-item label="商品分类" name="categoryId"> <a-form-item label="商品分类" name="categoryId" v-if="!merchantId">
<SelectGoodsCategory <SelectGoodsCategory
:data="data" :data="data"
placeholder="请选择商品分类" placeholder="请选择商品分类"
@@ -73,6 +74,15 @@
@done="chooseGoodsCategory" @done="chooseGoodsCategory"
/> />
</a-form-item> </a-form-item>
<a-form-item label="菜品分类" name="categoryId" v-else>
<SelectGoodsCategory
:merchantId="merchantId"
placeholder="请选择商品分类"
style="width: 558px"
v-model:value="form.categoryId"
@done="chooseTakeawayCategory"
/>
</a-form-item>
<a-form-item label="商品卖点" name="comments"> <a-form-item label="商品卖点" name="comments">
<a-input <a-input
allow-clear allow-clear
@@ -335,6 +345,9 @@
import { GoodsSpec } from "@/api/shop/goodsSpec/model"; import { GoodsSpec } from "@/api/shop/goodsSpec/model";
import { generateGoodsSku, listGoodsSku } from "@/api/shop/goodsSku"; import { generateGoodsSku, listGoodsSku } from "@/api/shop/goodsSku";
import { listGoodsSpec } from "@/api/shop/goodsSpec"; import { listGoodsSpec } from "@/api/shop/goodsSpec";
import CategorySelect from "@/views/cms/article/components/category-select.vue";
import { GoodsCategory } from "@/api/shop/goodsCategory/model";
import { listGoodsCategory } from "@/api/shop/goodsCategory";
// 是否是修改 // 是否是修改
const isUpdate = ref(false); const isUpdate = ref(false);
@@ -348,6 +361,8 @@
visible: boolean; visible: boolean;
// 修改回显的数据 // 修改回显的数据
data?: Goods | null; data?: Goods | null;
// 商户ID
merchantId?: number;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@@ -375,6 +390,7 @@
const files = ref<ItemType[]>([]); const files = ref<ItemType[]>([]);
const goodsSpec = ref<GoodsSpec>(); const goodsSpec = ref<GoodsSpec>();
const category = ref<string[]>([]); const category = ref<string[]>([]);
const takeaway = ref<GoodsCategory[]>([]);
const columns = [ const columns = [
{ {
@@ -552,11 +568,16 @@
form.merchantId = item.merchantId; form.merchantId = item.merchantId;
}; };
const chooseGoodsCategory = (item,value) => { const chooseGoodsCategory = (item: GoodsCategory,value: any) => {
form.categoryId = value[1].value; form.categoryId = value[1].value;
form.categoryParent = value[0].label; form.categoryParent = value[0].label;
form.categoryChildren = value[1].label; form.categoryChildren = value[1].label;
} }
const chooseTakeawayCategory = (item: GoodsCategory, value: any) => {
form.categoryParent = '店铺分类';
form.categoryChildren = value[0].label;
form.categoryId = item[0];
}
const chooseImage = (data: FileRecord) => { const chooseImage = (data: FileRecord) => {
images.value.push({ images.value.push({
@@ -673,9 +694,7 @@
const editorRef = ref<InstanceType<typeof TinymceEditor> | null>(null); const editorRef = ref<InstanceType<typeof TinymceEditor> | null>(null);
const config = ref({ const config = ref({
height: 240, height: 500,
plugins: 'code preview fullscreen searchreplace save autosave link autolink image media table codesample lists advlist charmap emoticons anchor directionality pagebreak quickbars nonbreaking visualblocks visualchars wordcount',
toolbar: false,
images_upload_handler: (blobInfo, success, error) => { images_upload_handler: (blobInfo, success, error) => {
const file = blobInfo.blob(); const file = blobInfo.blob();
const formData = new FormData(); const formData = new FormData();
@@ -686,6 +705,44 @@
error(msg); error(msg);
}) })
}, },
// 自定义文件上传(这里使用把选择的文件转成 blob 演示)
file_picker_callback: (callback: any, _value: any, meta: any) => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
// 设定文件可选类型
if (meta.filetype === 'image') {
input.setAttribute('accept', 'image/*');
} else if (meta.filetype === 'media') {
input.setAttribute('accept', 'video/*,.pdf');
}
input.onchange = () => {
const file = input.files?.[0];
if (!file) {
return;
}
if (meta.filetype === 'media') {
if (file.size / 1024 / 1024 > 200) {
editorRef.value?.alert({ content: '大小不能超过 200MB' });
return;
}
if(file.type.startsWith('application/pdf')){
uploadOss(file).then(res => {
const addPath = `<a href="${res.downloadUrl}" target="_blank">${res.name}</a>`;
content.value = content.value + addPath
})
return;
}
if (!file.type.startsWith('video/')) {
editorRef.value?.alert({ content: '只能选择视频文件' });
return;
}
uploadOss(file).then(res => {
callback(res.path)
});
}
};
input.click();
}
}); });
/* 粘贴图片上传服务器并插入编辑器 */ /* 粘贴图片上传服务器并插入编辑器 */
@@ -732,7 +789,8 @@
content: content.value, content: content.value,
files: JSON.stringify(files.value), files: JSON.stringify(files.value),
goodsSpec: goodsSpec.value, goodsSpec: goodsSpec.value,
goodsSkus: skuList.value goodsSkus: skuList.value,
type: props.merchantId ? 1 : 0
}; };
const saveOrUpdate = isUpdate.value ? updateGoods : addGoods; const saveOrUpdate = isUpdate.value ? updateGoods : addGoods;
saveOrUpdate(formData) saveOrUpdate(formData)
@@ -796,6 +854,10 @@
if (props.data.content){ if (props.data.content){
content.value = props.data.content; content.value = props.data.content;
} }
// 外卖商品分类
listGoodsCategory({merchantId: props.merchantId}).then(list => {
takeaway.value = list
})
isUpdate.value = true; isUpdate.value = true;
} else { } else {

View File

@@ -21,6 +21,7 @@
<SelectMerchant <SelectMerchant
:placeholder="`选择商户`" :placeholder="`选择商户`"
class="input-item" class="input-item"
v-if="!merchantId"
v-model:value="where.merchantName" v-model:value="where.merchantName"
@done="chooseMerchantId" @done="chooseMerchantId"
/> />
@@ -57,6 +58,7 @@
defineProps<{ defineProps<{
// 选中的角色 // 选中的角色
selection?: []; selection?: [];
merchantId?: number;
}>(), }>(),
{} {}
); );

View File

@@ -16,6 +16,7 @@
<search <search
@search="reload" @search="reload"
:selection="selection" :selection="selection"
:merchantId="merchantId"
@add="openEdit" @add="openEdit"
@remove="removeBatch" @remove="removeBatch"
@batchMove="openMove" @batchMove="openMove"
@@ -69,17 +70,21 @@
</a-card> </a-card>
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<GoodsEdit v-model:visible="showEdit" :data="current" @done="reload" /> <GoodsEdit
v-model:visible="showEdit"
:merchantId="merchantId"
:data="current"
@done="reload"
/>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { createVNode, ref } from 'vue'; import { createVNode, ref, watch } from 'vue';
import { message, Modal } from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro'; import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type { import type {
DatasourceFunction, DatasourceFunction,
ColumnItem ColumnItem
@@ -94,6 +99,7 @@
} from '@/api/shop/goods'; } from '@/api/shop/goods';
import type { Goods, GoodsParam } from '@/api/shop/goods/model'; import type { Goods, GoodsParam } from '@/api/shop/goods/model';
import { formatNumber } from 'ele-admin-pro/es'; import { formatNumber } from 'ele-admin-pro/es';
import router from '@/router';
// 表格实例 // 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -106,8 +112,8 @@
const showEdit = ref(false); const showEdit = ref(false);
// 是否显示批量移动弹窗 // 是否显示批量移动弹窗
const showMove = ref(false); const showMove = ref(false);
// 加载状态 // 店铺ID
const loading = ref(true); const merchantId = ref<number>();
// 表格数据源 // 表格数据源
const datasource: DatasourceFunction = ({ const datasource: DatasourceFunction = ({
@@ -120,6 +126,9 @@
if (filters) { if (filters) {
where.status = filters.status; where.status = filters.status;
} }
if (merchantId.value) {
where.merchantId = merchantId.value;
}
return pageGoods({ return pageGoods({
...where, ...where,
...orders, ...orders,
@@ -362,11 +371,6 @@
}); });
}; };
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */ /* 自定义行属性 */
const customRow = (record: Goods) => { const customRow = (record: Goods) => {
return { return {
@@ -380,7 +384,15 @@
} }
}; };
}; };
query();
watch(
() => router.currentRoute.value.params.id,
(id) => {
merchantId.value = Number(id);
reload();
},
{ immediate: true }
);
</script> </script>
<script lang="ts"> <script lang="ts">

View File

@@ -0,0 +1,292 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="620"
:visible="visible"
:confirm-loading="loading"
:title="isUpdate ? '修改分类' : '新建分类'"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 4, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
"
>
<a-row :gutter="16">
<a-col
v-bind="styleResponsive ? { md: 24, sm: 24, xs: 24 } : { span: 12 }"
>
<a-form-item label="分类名称" name="title">
<a-input
allow-clear
placeholder="请输入分类名称"
v-model:value="form.title"
@pressEnter="save"
/>
</a-form-item>
</a-col>
<a-col
v-bind="styleResponsive ? { md: 24, sm: 24, xs: 24 } : { span: 12 }"
>
<a-form-item label="排序号" name="sortNumber">
<a-input-number
:min="0"
:max="99999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="是否展示">
<a-switch
checked-children=""
un-checked-children=""
:checked="form.status === 0"
@update:checked="updateHideValue"
/>
</a-form-item>
<a-form-item label="分类图标" name="image" extra="尺寸180*180">
<SelectFile
:placeholder="`请选择图片`"
:limit="1"
:data="images"
@done="chooseFile"
@del="onDeleteItem"
/>
</a-form-item>
</a-col>
</a-row>
<div style="margin-bottom: 22px">
<a-divider />
</div>
<a-form-item
label="备注"
name="comments"
:label-col="
styleResponsive ? { md: 3, sm: 4, xs: 24 } : { flex: '90px' }
"
:wrapper-col="
styleResponsive ? { md: 21, sm: 20, xs: 24 } : { flex: '1' }
"
>
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入备注信息"
v-model:value="form.comments"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { message } from 'ant-design-vue/es';
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
import { storeToRefs } from 'pinia';
import { useThemeStore } from '@/store/modules/theme';
import useFormData from '@/utils/use-form-data';
import { GoodsCategory } from '@/api/shop/goodsCategory/model';
import {
addGoodsCategory,
updateGoodsCategory
} from '@/api/shop/goodsCategory';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FileRecord } from '@/api/system/file/model';
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
// 已上传数据
const images = ref<ItemType[]>([]);
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: GoodsCategory | null;
// 上级分类id
parentId?: number;
// 商户ID
merchantId?: number;
// 全部分类数据
categoryList: GoodsCategory[];
}>();
//
const formRef = ref<FormInstance | null>(null);
// 是否是修改
const isUpdate = ref(false);
// 提交状态
const loading = ref(false);
// 表单数据
const { form, resetFields, assignFields } = useFormData<GoodsCategory>({
categoryId: undefined,
title: '',
parentId: undefined,
image: '',
status: 0,
sortNumber: 100
});
// 表单验证规则
const rules = reactive<Record<string, Rule[]>>({
title: [
{
required: true,
message: '请输入分类名称',
type: 'string',
trigger: 'blur'
}
],
sortNumber: [
{
required: true,
message: '请输入排序号',
type: 'number',
trigger: 'blur'
}
],
meta: [
{
type: 'string',
validator: async (_rule: Rule, value: string) => {
if (value) {
const msg = '请输入正确的JSON格式';
try {
const obj = JSON.parse(value);
if (typeof obj !== 'object' || obj === null) {
return Promise.reject(msg);
}
} catch (_e) {
return Promise.reject(msg);
}
}
return Promise.resolve();
},
trigger: 'blur'
}
]
});
const chooseFile = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const categoryForm = {
...form,
// menuType 对应的值与后端不一致在前端处理
// menuType: form.menuType === 2 ? 1 : 0,
parentId: form.parentId || 0,
merchantId: props.merchantId
};
const saveOrUpdate = isUpdate.value
? updateGoodsCategory
: addGoodsCategory;
saveOrUpdate(categoryForm)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const updateHideValue = (value: boolean) => {
form.status = value ? 0 : 1;
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
assignFields({
...props.data,
parentId:
props.data.parentId === 0 ? undefined : props.data.parentId
});
images.value = [];
if (props.data.image) {
images.value.push({
uid: `${props.data.categoryId}`,
url: props.data.image,
status: 'done'
});
}
isUpdate.value = true;
} else {
images.value = [];
form.parentId = props.parentId;
isUpdate.value = false;
}
} else {
resetFields();
formRef.value?.clearValidate();
}
}
);
</script>
<script lang="ts">
import * as icons from '@/layout/menu-icons';
export default {
components: icons,
data() {
return {
iconData: [
{
title: '已引入的图标',
icons: Object.keys(icons)
}
]
};
}
};
</script>

View File

@@ -0,0 +1,181 @@
<!-- 搜索表单 -->
<template>
<a-space>
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<plus-outlined />
</template>
<span>新建</span>
</a-button>
<template v-if="!merchantId">
<a-button type="dashed" class="ele-btn-icon" @click="expandAll">
展开全部
</a-button>
<a-button type="dashed" class="ele-btn-icon" @click="foldAll">
折叠全部
</a-button>
</template>
<SelectMerchant
:placeholder="`选择商户`"
class="input-item"
v-if="!merchantId"
v-model:value="where.merchantName"
@done="chooseMerchantId"
/>
<!-- <SelectGoodsCategory-->
<!-- v-if="!merchantId"-->
<!-- class="input-item"-->
<!-- :placeholder="`请选择商品分类`"-->
<!-- v-model:value="where.categoryId"-->
<!-- @done="chooseGoodsCategory"-->
<!-- />-->
<a-input-search
allow-clear
placeholder="请输入关键词"
v-model:value="where.keywords"
@pressEnter="reload"
@search="reload"
/>
</a-space>
<!-- <a-space :size="10" style="flex-wrap: wrap">-->
<!-- <a-button type="primary" class="ele-btn-icon" @click="add">-->
<!-- <template #icon>-->
<!-- <PlusOutlined />-->
<!-- </template>-->
<!-- <span>添加</span>-->
<!-- </a-button>-->
<!-- <a-radio-group v-model:value="type" @change="handleSearch">-->
<!-- <a-radio-button value="出售中"-->
<!-- >出售中({{ goodsCount?.totalNum }})</a-radio-button-->
<!-- >-->
<!-- <a-radio-button value="待上架"-->
<!-- >待上架({{ goodsCount?.totalNum2 }})</a-radio-button-->
<!-- >-->
<!-- <a-radio-button value="已售罄"-->
<!-- >已售罄({{ goodsCount?.totalNum3 }})</a-radio-button-->
<!-- >-->
<!-- </a-radio-group>-->
<!-- <SelectMerchant-->
<!-- :placeholder="`选择商户`"-->
<!-- class="input-item"-->
<!-- v-if="!merchantId"-->
<!-- v-model:value="where.merchantName"-->
<!-- @done="chooseMerchantId"-->
<!-- />-->
<!-- <a-button @click="reset">重置</a-button>-->
<!-- </a-space>-->
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { ref, watch } from 'vue';
import { getCount } from '@/api/shop/goods';
import type { GoodsCount, GoodsParam } from '@/api/shop/goods/model';
import useSearch from '@/utils/use-search';
import { useRouter } from 'vue-router';
import { Merchant } from '@/api/shop/merchant/model';
import { GoodsCategory } from '@/api/shop/goodsCategory/model';
const { currentRoute } = useRouter();
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
merchantId?: number;
}>(),
{}
);
const type = ref<string>();
// 统计数据
const goodsCount = ref<GoodsCount>();
// 表单数据
const { where, resetFields } = useSearch<GoodsParam>({
goodsId: undefined,
isShow: undefined,
stock: undefined,
categoryId: undefined,
keywords: ''
});
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
(e: 'expand'): void;
(e: 'fold'): void;
}>();
const expandAll = () => {
emit('expand');
};
const foldAll = () => {
emit('fold');
};
// 新增
const add = () => {
emit('add');
};
const handleSearch = (e) => {
const text = e.target.value;
resetFields();
if (text === '出售中') {
where.isShow = 1;
}
if (text === '待上架') {
where.isShow = 0;
}
if (text === '已售罄') {
where.stock = 0;
}
emit('search', where);
};
const reload = () => {
getCount().then((data: any) => {
goodsCount.value = data;
});
emit('search', where);
};
/* 搜索 */
const chooseMerchantId = (item: Merchant) => {
where.merchantName = item.merchantName;
where.merchantId = item.merchantId;
reload();
};
const chooseGoodsCategory = (
category: GoodsCategory,
data: GoodsCategory
) => {
where.categoryName = data[1].label;
where.categoryId = data[1].value;
reload();
};
/* 重置 */
const reset = () => {
resetFields();
type.value = '';
reload();
};
// watch(
// () => props.selection,
// () => {}
// );
watch(
currentRoute,
() => {
reload();
},
{ immediate: true }
);
</script>

View File

@@ -18,28 +18,13 @@
@expand="onExpand" @expand="onExpand"
> >
<template #toolbar> <template #toolbar>
<a-space> <search
<a-button type="primary" class="ele-btn-icon" @click="openEdit()">
<template #icon>
<plus-outlined />
</template>
<span>新建</span>
</a-button>
<a-button type="dashed" class="ele-btn-icon" @click="expandAll">
展开全部
</a-button>
<a-button type="dashed" class="ele-btn-icon" @click="foldAll">
折叠全部
</a-button>
<!-- 搜索表单 -->
<a-input-search
allow-clear
v-model:value="searchText"
placeholder="请输入搜索关键词"
@search="reload" @search="reload"
@pressEnter="reload" :merchantId="merchantId"
@add="openEdit"
@expand="expandAll"
@fold="foldAll"
/> />
</a-space>
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'path'"> <template v-if="column.key === 'path'">
@@ -66,9 +51,7 @@
style="margin-right: 10px" style="margin-right: 10px"
v-if="record.image" v-if="record.image"
/> />
<a @click="openPreview(`/goods/search?categoryId=${record.categoryId}`)">{{ {{ record.title }}
record.title
}}</a>
</template> </template>
<template v-if="column.key === 'showIndex'"> <template v-if="column.key === 'showIndex'">
<a-space @click="onShowIndex(record)"> <a-space @click="onShowIndex(record)">
@@ -110,19 +93,30 @@
</template> </template>
</ele-pro-table> </ele-pro-table>
</a-card> </a-card>
<!-- 编辑弹窗 --> <!-- 商城分类编辑弹窗 -->
<category-edit <category-edit
v-model:visible="showEdit" v-model:visible="showEdit"
v-if="merchantId == 0"
:data="current" :data="current"
:parent-id="parentId" :parent-id="parentId"
:category-list="categoryData" :category-list="categoryData"
@done="reload" @done="reload"
/> />
<!-- 店铺分类编辑弹窗 -->
<merchant-category-edit
v-model:visible="showEdit"
v-else
:data="current"
:parent-id="parentId"
:merchantId="merchantId"
:category-list="categoryData"
@done="reload"
/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref, watch } from 'vue';
import { message } from 'ant-design-vue/es'; import { message } from 'ant-design-vue/es';
import { import {
ArrowUpOutlined, ArrowUpOutlined,
@@ -144,6 +138,7 @@
} from 'ele-admin-pro/es'; } from 'ele-admin-pro/es';
import type { EleProTable } from 'ele-admin-pro/es'; import type { EleProTable } from 'ele-admin-pro/es';
import CategoryEdit from './components/category-edit.vue'; import CategoryEdit from './components/category-edit.vue';
import MerchantCategoryEdit from './components/merchant-category-edit.vue';
import { import {
listGoodsCategory, listGoodsCategory,
removeGoodsCategory, removeGoodsCategory,
@@ -155,6 +150,8 @@
} from '@/api/shop/goodsCategory/model'; } from '@/api/shop/goodsCategory/model';
import { openNew, openPreview } from '@/utils/common'; import { openNew, openPreview } from '@/utils/common';
import { getSiteInfo } from '@/api/layout'; import { getSiteInfo } from '@/api/layout';
import router from '@/router';
import Search from './components/search.vue';
// 表格实例 // 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -251,6 +248,7 @@
const searchText = ref(''); const searchText = ref('');
const tenantId = ref<number>(); const tenantId = ref<number>();
const domain = ref<string>(); const domain = ref<string>();
const merchantId = ref<number>();
getSiteInfo().then((data) => { getSiteInfo().then((data) => {
tenantId.value = data.tenantId; tenantId.value = data.tenantId;
@@ -260,6 +258,9 @@
// 表格数据源 // 表格数据源
const datasource: DatasourceFunction = ({ where }) => { const datasource: DatasourceFunction = ({ where }) => {
where.title = searchText.value; where.title = searchText.value;
if (merchantId.value) {
where.merchantId = merchantId.value;
}
return listGoodsCategory({ ...where }); return listGoodsCategory({ ...where });
}; };
@@ -397,6 +398,15 @@
} }
}; };
}; };
watch(
() => router.currentRoute.value.params.id,
(id) => {
merchantId.value = Number(id);
reload();
},
{ immediate: true }
);
</script> </script>
<script lang="ts"> <script lang="ts">

View File

@@ -10,6 +10,12 @@
<a-button class="ele-btn-icon" @click="openUrl(`/shop/index`)"> <a-button class="ele-btn-icon" @click="openUrl(`/shop/index`)">
<span>店铺管理</span> <span>店铺管理</span>
</a-button> </a-button>
<a-button class="ele-btn-icon" @click="openUrl(`/shop/index`)">
<span>门店职员</span>
</a-button>
<a-button class="ele-btn-icon" @click="openUrl(`/goods/category/:id`)">
<span>商品分类</span>
</a-button>
<a-button class="ele-btn-icon" @click="openUrl(`/shop/apply`)"> <a-button class="ele-btn-icon" @click="openUrl(`/shop/apply`)">
<span>入驻申请</span> <span>入驻申请</span>
</a-button> </a-button>
@@ -28,6 +34,7 @@
import { watch } from 'vue'; import { watch } from 'vue';
import { openUrl } from '@/utils/common'; import { openUrl } from '@/utils/common';
import router from '@/router'; import router from '@/router';
import { getMerchant } from '@/api/shop/merchant';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -52,7 +59,7 @@
watch( watch(
() => router.currentRoute, () => router.currentRoute,
(route) => { (route) => {
console.log(route,'route'); console.log(route, 'route');
} }
); );
</script> </script>

View File

@@ -30,8 +30,12 @@
</template> </template>
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-space> <a-space>
<a @click="openUrl(`/shop/account/${record.merchantId}`)" <a @click="openUrl(`/goods/category/${record.merchantId}`)"
>门店用户</a >分类</a
>
<a-divider type="vertical" />
<a @click="openUrl(`/goods/index/${record.merchantId}`)"
>商品</a
> >
<a-divider type="vertical" /> <a-divider type="vertical" />
<a @click="openEdit(record)">修改</a> <a @click="openEdit(record)">修改</a>
@@ -55,7 +59,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { createVNode, ref } from 'vue'; import { createVNode, ref, watch } from 'vue';
import { message, Modal } from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro'; import type { EleProTable } from 'ele-admin-pro';
@@ -72,7 +76,8 @@
removeBatchMerchant removeBatchMerchant
} from '@/api/shop/merchant'; } from '@/api/shop/merchant';
import type { Merchant, MerchantParam } from '@/api/shop/merchant/model'; import type { Merchant, MerchantParam } from '@/api/shop/merchant/model';
import { openNew, openPreview, openUrl } from '@/utils/common'; import { openUrl } from '@/utils/common';
import router from '@/router';
// 表格实例 // 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -85,8 +90,8 @@
const showEdit = ref(false); const showEdit = ref(false);
// 是否显示批量移动弹窗 // 是否显示批量移动弹窗
const showMove = ref(false); const showMove = ref(false);
// 加载状态 // 商户ID
const loading = ref(true); const merchantId = ref(0);
// 表格数据源 // 表格数据源
const datasource: DatasourceFunction = ({ const datasource: DatasourceFunction = ({
@@ -169,7 +174,7 @@
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
width: 180, width: 240,
fixed: 'right', fixed: 'right',
align: 'center', align: 'center',
hideInSetting: true hideInSetting: true
@@ -235,11 +240,6 @@
}); });
}; };
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */ /* 自定义行属性 */
const customRow = (record: Merchant) => { const customRow = (record: Merchant) => {
return { return {
@@ -253,7 +253,20 @@
} }
}; };
}; };
query();
watch(
() => router.currentRoute.value.params.id,
(id) => {
if (id) {
if (id == ':id') {
merchantId.value = 0;
} else {
merchantId.value = Number(id);
}
}
},
{ immediate: true }
);
</script> </script>
<script lang="ts"> <script lang="ts">

View File

@@ -7,24 +7,23 @@
row-key="logId" row-key="logId"
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
v-model:selection="selection"
:scroll="{ x: 1000 }" :scroll="{ x: 1000 }"
:where="defaultWhere" :where="defaultWhere"
cache-key="userBalanceLogTable" cache-key="userBalanceLogTable"
> >
<template #toolbar> <template #toolbar>
<a-space> <a-space>
<a-button <!-- <a-button-->
danger <!-- danger-->
type="primary" <!-- type="primary"-->
class="ele-btn-icon" <!-- class="ele-btn-icon"-->
@click="removeBatch" <!-- @click="removeBatch"-->
> <!-- >-->
<template #icon> <!-- <template #icon>-->
<delete-outlined /> <!-- <delete-outlined />-->
</template> <!-- </template>-->
<span>批量删除</span> <!-- <span>批量删除</span>-->
</a-button> <!-- </a-button>-->
<a-range-picker <a-range-picker
v-model:value="dateRange" v-model:value="dateRange"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@@ -36,7 +35,6 @@
placeholder="请输入关键词" placeholder="请输入关键词"
@search="reload" @search="reload"
@pressEnter="reload" @pressEnter="reload"
@close="onClose"
/> />
<a-button @click="reset">重置</a-button> <a-button @click="reset">重置</a-button>
</a-space> </a-space>
@@ -128,13 +126,19 @@
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格列配置 // 表格列配置
const columns = ref<ColumnItem[]>([ const columns = ref<ColumnItem[]>([
// {
// key: 'index',
// width: 48,
// align: 'center',
// fixed: 'left',
// hideInSetting: true,
// customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
// },
{ {
key: 'index', title: '用户ID',
width: 48, dataIndex: 'userId',
align: 'center', width: 80,
fixed: 'left', showSorterTooltip: false
hideInSetting: true,
customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
}, },
{ {
title: '用户', title: '用户',