feat: (web-ele)新增颜色输入框组件并优化图片上传组件
- 新增 ColorInput 组件用于颜色选择 - 重构 ImageUpload 组件,增加编辑和删除功能 - 更新 DIY 编辑器相关组件,优化用户体验 - 添加商城 H5 预览地址配置 - 优化导航栏单元格属性配置
This commit is contained in:
@@ -3,6 +3,10 @@ import type { MallSpuApi } from '#/api/mall/product/spu';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { ElImage, ElTooltip } from 'element-plus';
|
||||
|
||||
import * as ProductSpuApi from '#/api/mall/product/spu';
|
||||
import SpuTableSelect from '#/views/mall/product/spu/components/spu-table-select.vue';
|
||||
|
||||
@@ -110,23 +114,23 @@ const emitSpuChange = () => {
|
||||
:key="spu.id"
|
||||
class="select-box spu-pic"
|
||||
>
|
||||
<el-tooltip :content="spu.name">
|
||||
<ElTooltip :content="spu.name">
|
||||
<div class="relative h-full w-full">
|
||||
<el-image :src="spu.picUrl" class="h-full w-full" />
|
||||
<Icon
|
||||
<ElImage :src="spu.picUrl" class="h-full w-full" />
|
||||
<IconifyIcon
|
||||
v-show="!disabled"
|
||||
class="del-icon"
|
||||
icon="ep:circle-close-filled"
|
||||
@click="handleRemoveSpu(index)"
|
||||
/>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
<el-tooltip content="选择商品" v-if="canAdd">
|
||||
<ElTooltip content="选择商品" v-if="canAdd">
|
||||
<div class="select-box" @click="openSpuTableSelect">
|
||||
<Icon icon="ep:plus" />
|
||||
<IconifyIcon icon="ep:plus" />
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
<!-- 商品选择对话框(表格形式) -->
|
||||
<SpuTableSelect
|
||||
|
||||
@@ -6,7 +6,21 @@ import { onMounted, ref } from 'vue';
|
||||
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { CHANGE_EVENT } from 'element-plus';
|
||||
import {
|
||||
CHANGE_EVENT,
|
||||
ElButton,
|
||||
ElCheckbox,
|
||||
ElDatePicker,
|
||||
ElDialog,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElImage,
|
||||
ElInput,
|
||||
ElRadio,
|
||||
ElTable,
|
||||
ElTableColumn,
|
||||
ElTreeSelect,
|
||||
} from 'element-plus';
|
||||
|
||||
import * as ProductCategoryApi from '#/api/mall/product/category';
|
||||
import * as ProductSpuApi from '#/api/mall/product/spu';
|
||||
@@ -210,30 +224,30 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:append-to-body="true"
|
||||
title="选择商品"
|
||||
width="70%"
|
||||
>
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
<ElForm
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
class="-mb-15px"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="商品名称" prop="name">
|
||||
<el-input
|
||||
<ElFormItem label="商品名称" prop="name">
|
||||
<ElInput
|
||||
v-model="queryParams.name"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入商品名称"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品分类" prop="categoryId">
|
||||
<el-tree-select
|
||||
</ElFormItem>
|
||||
<ElFormItem label="商品分类" prop="categoryId">
|
||||
<ElTreeSelect
|
||||
v-model="queryParams.categoryId"
|
||||
:data="categoryTreeList"
|
||||
:props="{
|
||||
@@ -248,9 +262,9 @@ onMounted(async () => {
|
||||
node-key="id"
|
||||
placeholder="请选择商品分类"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
</ElFormItem>
|
||||
<ElFormItem label="创建时间" prop="createTime">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.createTime"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
@@ -259,67 +273,67 @@ onMounted(async () => {
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery">
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton @click="handleQuery">
|
||||
<Icon class="mr-5px" icon="ep:search" />
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
</ElButton>
|
||||
<ElButton @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>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<ElTable v-loading="loading" :data="list" show-overflow-tooltip>
|
||||
<!-- 1. 多选模式(不能使用type="selection",Element会忽略Header插槽) -->
|
||||
<el-table-column width="55" v-if="multiple">
|
||||
<ElTableColumn width="55" v-if="multiple">
|
||||
<template #header>
|
||||
<el-checkbox
|
||||
<ElCheckbox
|
||||
v-model="isCheckAll"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAll"
|
||||
/>
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<el-checkbox
|
||||
<ElCheckbox
|
||||
v-model="checkedStatus[row.id]"
|
||||
@change="(checked: boolean) => handleCheckOne(checked, row, true)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ElTableColumn>
|
||||
<!-- 2. 单选模式 -->
|
||||
<el-table-column label="#" width="55" v-else>
|
||||
<ElTableColumn label="#" width="55" v-else>
|
||||
<template #default="{ row }">
|
||||
<el-radio
|
||||
<ElRadio
|
||||
:value="row.id"
|
||||
v-model="selectedSpuId"
|
||||
@change="handleSingleSelected(row)"
|
||||
>
|
||||
<!-- 空格不能省略,是为了让单选框不显示label,如果不指定label不会有选中的效果 -->
|
||||
|
||||
</el-radio>
|
||||
</ElRadio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
</ElTableColumn>
|
||||
<ElTableColumn
|
||||
key="id"
|
||||
align="center"
|
||||
label="商品编号"
|
||||
prop="id"
|
||||
min-width="60"
|
||||
/>
|
||||
<el-table-column label="商品图" min-width="80">
|
||||
<ElTableColumn label="商品图" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
<ElImage
|
||||
: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">
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="商品名称" min-width="200" prop="name" />
|
||||
<ElTableColumn label="商品分类" min-width="100" prop="categoryId">
|
||||
<template #default="{ row }">
|
||||
<span>{{
|
||||
categoryList?.find(
|
||||
@@ -327,8 +341,8 @@ onMounted(async () => {
|
||||
)?.name
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@@ -338,8 +352,8 @@ onMounted(async () => {
|
||||
/>
|
||||
</ContentWrap>
|
||||
<template #footer v-if="multiple">
|
||||
<el-button type="primary" @click="handleEmitChange">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<ElButton type="primary" @click="handleEmitChange">确 定</ElButton>
|
||||
<ElButton @click="dialogVisible = false">取 消</ElButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</ElDialog>
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useRoute } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import * as DiyPageApi from '#/api/mall/promotion/diy/page';
|
||||
import { PAGE_LIBS } from '#/components/DiyEditor/util';
|
||||
import { PAGE_LIBS } from '#/components/diy-editor/util';
|
||||
|
||||
/** 装修页面表单 */
|
||||
defineOptions({ name: 'DiyPageDecorate' });
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import * as DiyPageApi from '@/api/mall/promotion/diy/page';
|
||||
import type { MallDiyPageApi } from '#/api/mall/promotion/diy/page';
|
||||
import type { MallDiyTemplateApi } from '#/api/mall/promotion/diy/template';
|
||||
import type { DiyComponentLibrary } from '#/components/diy-editor/util'; // 商城的 DIY 组件,在 DiyEditor 目录下
|
||||
|
||||
import { onMounted, reactive, ref, unref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import * as DiyPageApi from '#/api/mall/promotion/diy/page';
|
||||
// TODO @疯狂:要不要建个 decorate 目录,然后挪进去,改成 index.vue,这样可以更明确看到是个独立界面哈,更好找
|
||||
import * as DiyTemplateApi from '@/api/mall/promotion/diy/template';
|
||||
import { DiyComponentLibrary, PAGE_LIBS } from '@/components/DiyEditor/util'; // 商城的 DIY 组件,在 DiyEditor 目录下
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
import { isEmpty } from '@/utils/is';
|
||||
import { toNumber } from 'lodash-es';
|
||||
import * as DiyTemplateApi from '#/api/mall/promotion/diy/template';
|
||||
import DiyEditor from '#/components/diy-editor/index.vue';
|
||||
import { PAGE_LIBS } from '#/components/diy-editor/util';
|
||||
|
||||
/** 装修模板表单 */
|
||||
defineOptions({ name: 'DiyTemplateDecorate' });
|
||||
@@ -18,20 +28,18 @@ const templateItems = reactive([
|
||||
{ name: '我的', icon: 'ep:user-filled' },
|
||||
]);
|
||||
|
||||
const message = useMessage(); // 消息弹窗
|
||||
|
||||
const formLoading = ref(false); // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref<DiyTemplateApi.DiyTemplatePropertyVO>();
|
||||
const formData = ref<MallDiyTemplateApi.DiyTemplateProperty>();
|
||||
const formRef = ref(); // 表单 Ref
|
||||
// 当前编辑的属性
|
||||
const currentFormData = ref<
|
||||
DiyPageApi.DiyPageVO | DiyTemplateApi.DiyTemplatePropertyVO
|
||||
MallDiyPageApi.DiyPage | MallDiyTemplateApi.DiyTemplateProperty
|
||||
>({
|
||||
property: '',
|
||||
} as DiyPageApi.DiyPageVO);
|
||||
} as MallDiyPageApi.DiyPage);
|
||||
// templateItem 对应的缓存
|
||||
const currentFormDataMap = ref<
|
||||
Map<string, DiyPageApi.DiyPageVO | DiyTemplateApi.DiyTemplatePropertyVO>
|
||||
Map<string, MallDiyPageApi.DiyPage | MallDiyTemplateApi.DiyTemplateProperty>
|
||||
>(new Map());
|
||||
// 商城 H5 预览地址
|
||||
const previewUrl = ref('');
|
||||
@@ -57,11 +65,11 @@ const libs = ref<DiyComponentLibrary[]>(templateLibs);
|
||||
const handleTemplateItemChange = (val: number) => {
|
||||
// 缓存模版编辑数据
|
||||
currentFormDataMap.value.set(
|
||||
templateItems[selectedTemplateItem.value].name,
|
||||
templateItems[selectedTemplateItem.value]?.name || '',
|
||||
currentFormData.value!,
|
||||
);
|
||||
// 读取模版缓存
|
||||
const data = currentFormDataMap.value.get(templateItems[val].name);
|
||||
const data = currentFormDataMap.value.get(templateItems[val]?.name || '');
|
||||
|
||||
// 切换模版
|
||||
selectedTemplateItem.value = val;
|
||||
@@ -69,8 +77,8 @@ const handleTemplateItemChange = (val: number) => {
|
||||
if (val === 0) {
|
||||
libs.value = templateLibs;
|
||||
currentFormData.value = (isEmpty(data) ? formData.value : data) as
|
||||
| DiyPageApi.DiyPageVO
|
||||
| DiyTemplateApi.DiyTemplatePropertyVO;
|
||||
| MallDiyPageApi.DiyPage
|
||||
| MallDiyTemplateApi.DiyTemplateProperty;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -79,16 +87,17 @@ const handleTemplateItemChange = (val: number) => {
|
||||
currentFormData.value = (
|
||||
isEmpty(data)
|
||||
? formData.value!.pages.find(
|
||||
(page: DiyPageApi.DiyPageVO) => page.name === templateItems[val].name,
|
||||
(page: MallDiyPageApi.DiyPage) =>
|
||||
page.name === templateItems[val]?.name,
|
||||
)
|
||||
: data
|
||||
) as DiyPageApi.DiyPageVO | DiyTemplateApi.DiyTemplatePropertyVO;
|
||||
) as MallDiyPageApi.DiyPage | MallDiyTemplateApi.DiyTemplateProperty;
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return;
|
||||
if (!formRef.value) return;
|
||||
// 提交请求
|
||||
formLoading.value = true;
|
||||
try {
|
||||
@@ -114,7 +123,7 @@ const submitForm = async () => {
|
||||
await DiyPageApi.updateDiyPageProperty(data!);
|
||||
}
|
||||
}
|
||||
message.success('保存成功');
|
||||
ElMessage.success('保存成功');
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
@@ -131,7 +140,7 @@ const resetForm = () => {
|
||||
previewPicUrls: [],
|
||||
property: '',
|
||||
pages: [],
|
||||
} as DiyTemplateApi.DiyTemplatePropertyVO;
|
||||
} as MallDiyTemplateApi.DiyTemplateProperty;
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
|
||||
@@ -147,17 +156,17 @@ const storePageIndex = () =>
|
||||
// 2. 恢复
|
||||
const recoverPageIndex = () => {
|
||||
// 恢复重置前的页面,默认是第一个页面
|
||||
const pageIndex = toNumber(sessionStorage.getItem(DIY_PAGE_INDEX_KEY)) || 0;
|
||||
const pageIndex = Number(sessionStorage.getItem(DIY_PAGE_INDEX_KEY)) || 0;
|
||||
// 移除标记
|
||||
sessionStorage.removeItem(DIY_PAGE_INDEX_KEY);
|
||||
|
||||
// 重新初始化数据
|
||||
currentFormData.value = formData.value as
|
||||
| DiyPageApi.DiyPageVO
|
||||
| DiyTemplateApi.DiyTemplatePropertyVO;
|
||||
| MallDiyPageApi.DiyPage
|
||||
| MallDiyTemplateApi.DiyTemplateProperty;
|
||||
currentFormDataMap.value = new Map<
|
||||
string,
|
||||
DiyPageApi.DiyPageVO | DiyTemplateApi.DiyTemplatePropertyVO
|
||||
MallDiyPageApi.DiyPage | MallDiyTemplateApi.DiyTemplateProperty
|
||||
>();
|
||||
// 切换页面
|
||||
if (pageIndex !== selectedTemplateItem.value) {
|
||||
@@ -168,15 +177,12 @@ const recoverPageIndex = () => {
|
||||
|
||||
/** 初始化 */
|
||||
const { currentRoute } = useRouter(); // 路由
|
||||
const { delView } = useTagsViewStore(); // 视图操作
|
||||
onMounted(async () => {
|
||||
resetForm();
|
||||
if (!currentRoute.value.params.id) {
|
||||
message.warning('参数错误,页面编号不能为空!');
|
||||
delView(unref(currentRoute));
|
||||
ElMessage.warning('参数错误,页面编号不能为空!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询详情
|
||||
await getPageDetail(currentRoute.value.params.id);
|
||||
// 恢复重置前的页面
|
||||
@@ -192,7 +198,7 @@ onMounted(async () => {
|
||||
:show-navigation-bar="selectedTemplateItem !== 0"
|
||||
:show-page-config="selectedTemplateItem !== 0"
|
||||
:show-tab-bar="selectedTemplateItem === 0"
|
||||
:title="templateItems[selectedTemplateItem].name"
|
||||
:title="templateItems[selectedTemplateItem]?.name || ''"
|
||||
@reset="handleEditorReset"
|
||||
@save="submitForm"
|
||||
>
|
||||
@@ -208,7 +214,7 @@ onMounted(async () => {
|
||||
:content="item.name"
|
||||
>
|
||||
<el-radio-button :value="index">
|
||||
<Icon :icon="item.icon" :size="24" />
|
||||
<IconifyIcon :icon="item.icon" :size="24" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
</el-radio-group>
|
||||
|
||||
@@ -3,6 +3,8 @@ import type { MallPointActivityApi } from '#/api/mall/promotion/point';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { ElImage, ElTooltip } from 'element-plus';
|
||||
|
||||
import * as PointActivityApi from '#/api/mall/promotion/point';
|
||||
|
||||
import PointTableSelect from './point-table-select.vue';
|
||||
@@ -123,23 +125,23 @@ const emitActivityChange = () => {
|
||||
:key="pointActivity.id"
|
||||
class="select-box spu-pic"
|
||||
>
|
||||
<el-tooltip :content="pointActivity.spuName">
|
||||
<ElTooltip :content="pointActivity.spuName">
|
||||
<div class="relative h-full w-full">
|
||||
<el-image :src="pointActivity.picUrl" class="h-full w-full" />
|
||||
<Icon
|
||||
<ElImage :src="pointActivity.picUrl" class="h-full w-full" />
|
||||
<IconifyIcon
|
||||
v-show="!disabled"
|
||||
class="del-icon"
|
||||
icon="ep:circle-close-filled"
|
||||
@click="handleRemoveActivity(index)"
|
||||
/>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
<el-tooltip v-if="canAdd" content="选择活动">
|
||||
<ElTooltip v-if="canAdd" content="选择活动">
|
||||
<div class="select-box" @click="openSeckillActivityTableSelect">
|
||||
<Icon icon="ep:plus" />
|
||||
<IconifyIcon icon="ep:plus" />
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
<!-- 拼团活动选择对话框(表格形式) -->
|
||||
<PointTableSelect
|
||||
|
||||
@@ -3,6 +3,10 @@ import type { MallSeckillActivityApi } from '#/api/mall/promotion/seckill/seckil
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { ElImage, ElTooltip } from 'element-plus';
|
||||
|
||||
import * as SeckillActivityApi from '#/api/mall/promotion/seckill/seckillActivity';
|
||||
import SeckillTableSelect from '#/views/mall/promotion/seckill/components/seckill-table-select.vue';
|
||||
|
||||
@@ -120,23 +124,23 @@ const emitActivityChange = () => {
|
||||
:key="seckillActivity.id"
|
||||
class="select-box spu-pic"
|
||||
>
|
||||
<el-tooltip :content="seckillActivity.name">
|
||||
<ElTooltip :content="seckillActivity.name">
|
||||
<div class="relative h-full w-full">
|
||||
<el-image :src="seckillActivity.picUrl" class="h-full w-full" />
|
||||
<Icon
|
||||
<ElImage :src="seckillActivity.picUrl" class="h-full w-full" />
|
||||
<IconifyIcon
|
||||
v-show="!disabled"
|
||||
class="del-icon"
|
||||
icon="ep:circle-close-filled"
|
||||
@click="handleRemoveActivity(index)"
|
||||
/>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
<el-tooltip content="选择活动" v-if="canAdd">
|
||||
<ElTooltip content="选择活动" v-if="canAdd">
|
||||
<div class="select-box" @click="openSeckillActivityTableSelect">
|
||||
<Icon icon="ep:plus" />
|
||||
<IconifyIcon icon="ep:plus" />
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
<!-- 拼团活动选择对话框(表格形式) -->
|
||||
<SeckillTableSelect
|
||||
|
||||
Reference in New Issue
Block a user