打包文件
This commit is contained in:
parent
7e4c1f7daa
commit
1ab653eebe
|
@ -2,13 +2,13 @@
|
|||
# https://cn.vitejs.dev/guide/env-and-mode.html#modes
|
||||
# NODE_ENV = development
|
||||
|
||||
VITE_PUBLIC_PATH = ./
|
||||
VITE_PUBLIC_PATH = /shop-admin/
|
||||
|
||||
# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
||||
VITE_ROUTER_HISTORY = "hash"
|
||||
|
||||
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
|
||||
VITE_CDN = true
|
||||
VITE_CDN = false
|
||||
|
||||
# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件)
|
||||
# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
|
||||
|
@ -16,4 +16,4 @@ VITE_CDN = true
|
|||
VITE_COMPRESSION = "none"
|
||||
|
||||
# 后端地址
|
||||
VITE_APP_BASE_API = '/stage-api'
|
||||
VITE_APP_BASE_API = '/shop-back-end'
|
|
@ -9,3 +9,4 @@ commitlint.config.js
|
|||
postcss.config.js
|
||||
tailwind.config.js
|
||||
stylelint.config.js
|
||||
build
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# shellcheck source=./_/husky.sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit "$1"
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/sh
|
||||
command_exists () {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Workaround for Windows 10, Git Bash and Pnpm
|
||||
if command_exists winpty && test -t 1; then
|
||||
exec < /dev/tty
|
||||
fi
|
|
@ -1,8 +0,0 @@
|
|||
module.exports = {
|
||||
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
|
||||
"{!(package)*.json}": ["prettier --write--parser json"],
|
||||
"package.json": ["prettier --write"],
|
||||
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
|
||||
"*.{vue,css,scss,postcss,less}": ["stylelint --fix", "prettier --write"],
|
||||
"*.md": ["prettier --write"]
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
. "$(dirname "$0")/common.sh"
|
||||
|
||||
[ -n "$CI" ] && exit 0
|
||||
|
||||
# Format and submit code according to lintstagedrc.js configuration
|
||||
npm run lint:lint-staged
|
||||
|
||||
npm run lint:pretty
|
|
@ -33,5 +33,10 @@
|
|||
"cSpell.words": [
|
||||
"iconify",
|
||||
"Qrcode"
|
||||
]
|
||||
],
|
||||
"marscode.codeCompletionPro": {
|
||||
"enableCodeCompletionPro": true
|
||||
},
|
||||
"marscode.enableInlineCommand": true,
|
||||
"marscode.chatLanguage": "cn"
|
||||
}
|
|
@ -17,15 +17,6 @@ export function viteBuildInfo(): Plugin {
|
|||
outDir = resolvedConfig.build?.outDir ?? "dist";
|
||||
},
|
||||
buildStart() {
|
||||
console.log(
|
||||
bold(
|
||||
green(
|
||||
`👏欢迎使用${blue(
|
||||
"[Agileboot全栈项目]"
|
||||
)},如果您感觉不错,记得点击后面链接给个star哦💖 https://github.com/valarchie/agileboot-back-end`
|
||||
)
|
||||
)
|
||||
);
|
||||
if (config.command === "build") {
|
||||
startTime = dayjs(new Date());
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ export interface CategoryDTO {
|
|||
createTime?: Date;
|
||||
updaterId?: number;
|
||||
updateTime?: Date;
|
||||
deleted?: number;
|
||||
}
|
||||
|
||||
/** 分类请求参数 */
|
||||
|
@ -54,7 +53,7 @@ export const updateCategoryApi = (categoryId: number, data: CategoryRequest) =>
|
|||
};
|
||||
|
||||
/** 删除分类 */
|
||||
export const deleteCategoryApi = (categoryId: number) => {
|
||||
export const deleteCategoryApi = (categoryId: number | string) => {
|
||||
return http.request<ResponseData<void>>("delete", `/shop/category/${categoryId}`);
|
||||
};
|
||||
|
||||
|
|
|
@ -10,13 +10,13 @@ export interface GoodsQuery extends BasePageQuery {
|
|||
/** 商品DTO */
|
||||
export interface GoodsDTO {
|
||||
goodsId?: number;
|
||||
goodsName?: string;
|
||||
categoryId?: number;
|
||||
price?: number;
|
||||
stock?: number;
|
||||
status?: number;
|
||||
coverImg?: string;
|
||||
goodsDetail?: string;
|
||||
goodsName: string;
|
||||
categoryId: number;
|
||||
price: number;
|
||||
stock: number;
|
||||
status: number;
|
||||
coverImg: string;
|
||||
goodsDetail: string;
|
||||
creatorId?: number;
|
||||
createTime?: Date;
|
||||
updaterId?: number;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>"
|
||||
);
|
||||
} catch (t) {
|
||||
console && console.log(t);
|
||||
// console && console.log(t);
|
||||
}
|
||||
}
|
||||
(n = function () {
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
<script setup lang="ts">
|
||||
import { reactive, ref, watch } from "vue";
|
||||
import { FormInstance, FormRules } from "element-plus";
|
||||
import { updateCategoryApi, addCategoryApi } from "@/api/shop/category";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import Confirm from "@iconify-icons/ep/check";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
row: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:visible", "refresh"]);
|
||||
|
||||
const isEdit = ref(false);
|
||||
const formRef = ref<FormInstance>();
|
||||
const formData = reactive({
|
||||
categoryId: 0,
|
||||
categoryName: "",
|
||||
sort: 0,
|
||||
description: ""
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
categoryName: [{
|
||||
required: true,
|
||||
message: "分类名称必填",
|
||||
trigger: "blur"
|
||||
}],
|
||||
sort: [{
|
||||
required: true,
|
||||
message: "排序值必填",
|
||||
trigger: "blur",
|
||||
type: "number"
|
||||
}]
|
||||
});
|
||||
|
||||
// 初始化表单数据
|
||||
const initForm = () => {
|
||||
if (props.row?.categoryId) {
|
||||
isEdit.value = true;
|
||||
Object.assign(formData, props.row);
|
||||
} else {
|
||||
isEdit.value = false;
|
||||
formData.categoryId = 0;
|
||||
formData.categoryName = "";
|
||||
formData.sort = 0;
|
||||
formData.description = "";
|
||||
}
|
||||
};
|
||||
|
||||
// 提交处理
|
||||
const handleConfirm = async () => {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
|
||||
if (isEdit.value) {
|
||||
await updateCategoryApi(formData.categoryId, formData);
|
||||
} else {
|
||||
await addCategoryApi(formData);
|
||||
}
|
||||
|
||||
ElMessage.success(`${isEdit.value ? "修改" : "新增"}成功`);
|
||||
emit("refresh");
|
||||
closeDialog();
|
||||
} catch (error) {
|
||||
console.error("表单提交失败", error);
|
||||
}
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
formRef.value?.resetFields();
|
||||
emit("update:visible", false);
|
||||
};
|
||||
|
||||
// 监听visible变化初始化表单
|
||||
watch(
|
||||
() => props.visible,
|
||||
val => {
|
||||
if (val) initForm();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog :title="isEdit ? '编辑分类' : '新增分类'" :model-value="visible" width="500px" @close="closeDialog">
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="80px" label-position="right">
|
||||
<el-form-item label="分类名称" prop="categoryName">
|
||||
<el-input v-model="formData.categoryName" placeholder="请输入分类名称" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" controls-position="right" class="!w-full" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入分类描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="closeDialog">取消</el-button>
|
||||
<el-button type="primary" :icon="useRenderIcon(Confirm)" @click="handleConfirm">
|
||||
确认
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
|
@ -0,0 +1,107 @@
|
|||
<script setup lang="ts">
|
||||
import { reactive, ref, watch } from "vue";
|
||||
import { FormInstance, FormRules } from "element-plus";
|
||||
import { addCategoryApi, updateCategoryApi } from "@/api/shop/category";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import Confirm from "@iconify-icons/ep/check";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
row: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:visible", "refresh"]);
|
||||
|
||||
const isEdit = ref(false);
|
||||
const formRef = ref<FormInstance>();
|
||||
const formData = reactive({
|
||||
categoryId: 0,
|
||||
categoryName: "",
|
||||
sort: 0,
|
||||
description: ""
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
categoryName: [{ required: true, message: "分类名称必填", trigger: "blur" }],
|
||||
sort: [{
|
||||
required: true,
|
||||
message: "排序值必填",
|
||||
trigger: "blur",
|
||||
type: "number"
|
||||
}]
|
||||
});
|
||||
|
||||
// 初始化表单数据
|
||||
const initFormData = () => {
|
||||
if (props.row) {
|
||||
Object.assign(formData, props.row);
|
||||
isEdit.value = true;
|
||||
} else {
|
||||
formData.categoryId = 0;
|
||||
formData.categoryName = "";
|
||||
formData.sort = 0;
|
||||
formData.description = "";
|
||||
isEdit.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 提交处理
|
||||
const handleConfirm = async () => {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
|
||||
if (isEdit.value) {
|
||||
await updateCategoryApi(formData.categoryId, formData);
|
||||
} else {
|
||||
await addCategoryApi(formData);
|
||||
}
|
||||
|
||||
ElMessage.success(`${isEdit.value ? "修改" : "新增"}成功`);
|
||||
emit("refresh");
|
||||
emit("update:visible", false);
|
||||
} catch (error) {
|
||||
console.error("表单提交失败", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 监听visible变化初始化表单
|
||||
watch(
|
||||
() => props.visible,
|
||||
val => {
|
||||
if (val) {
|
||||
initFormData();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog :title="isEdit ? '编辑分类' : '新增分类'" :model-value="visible" width="30%"
|
||||
@close="emit('update:visible', false)">
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="80px">
|
||||
<el-form-item label="分类名称" prop="categoryName">
|
||||
<el-input v-model="formData.categoryName" placeholder="请输入分类名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" controls-position="right" class="!w-full" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入分类描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="emit('update:visible', false)">取消</el-button>
|
||||
<el-button type="primary" :icon="useRenderIcon(Confirm)" @click="handleConfirm">
|
||||
确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
|
@ -0,0 +1,216 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import {
|
||||
getCategoryListApi,
|
||||
deleteCategoryApi,
|
||||
CategoryDTO
|
||||
} from "@/api/shop/category";
|
||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||
import Search from "@iconify-icons/ep/search";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
import CategoryFormModal from "./category-form-modal.vue";
|
||||
import CategoryEditModal from "./category-edit-modal.vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
defineOptions({
|
||||
name: "ShopCategory"
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const tableRef = ref();
|
||||
const modalVisible = ref(false);
|
||||
const editVisible = ref(false);
|
||||
const currentRow = ref<CategoryDTO>();
|
||||
|
||||
// 搜索表单
|
||||
const searchFormParams = ref({
|
||||
categoryName: "",
|
||||
sort: null
|
||||
});
|
||||
|
||||
// 分页参数
|
||||
const pagination = ref({
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0
|
||||
});
|
||||
|
||||
// 加载数据
|
||||
const loading = ref(false);
|
||||
const dataList = ref([]);
|
||||
const multipleSelection = ref<number[]>([]);
|
||||
|
||||
// API调用
|
||||
const getList = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { data } = await getCategoryListApi({
|
||||
...searchFormParams.value,
|
||||
pageSize: pagination.value.pageSize,
|
||||
pageNum: pagination.value.currentPage
|
||||
});
|
||||
dataList.value = data.rows;
|
||||
pagination.value.total = data.total;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const onSearch = () => {
|
||||
pagination.value.currentPage = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置
|
||||
const resetForm = () => {
|
||||
formRef.value.resetFields();
|
||||
getList();
|
||||
};
|
||||
|
||||
// 分页处理
|
||||
const onSizeChange = (val: number) => {
|
||||
pagination.value.pageSize = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
const onCurrentChange = (val: number) => {
|
||||
pagination.value.currentPage = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 删除处理
|
||||
const handleDelete = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确认删除分类【${row.categoryName}】?`,
|
||||
"提示",
|
||||
{
|
||||
type: "warning"
|
||||
}
|
||||
);
|
||||
await deleteCategoryApi(row.categoryId);
|
||||
ElMessage.success("删除成功");
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error("删除失败", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const handleBulkDelete = async () => {
|
||||
if (multipleSelection.value.length === 0) return;
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确认删除选中的${multipleSelection.value.length}项?`,
|
||||
"提示",
|
||||
{ type: "warning" }
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
multipleSelection.value.map(id => deleteCategoryApi(id))
|
||||
);
|
||||
|
||||
ElMessage.success("批量删除成功");
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error("批量删除失败", error);
|
||||
}
|
||||
};
|
||||
const handleEdit = (row: CategoryDTO) => {
|
||||
currentRow.value = row;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
label: "分类名称",
|
||||
prop: "categoryName"
|
||||
},
|
||||
{
|
||||
label: "排序",
|
||||
prop: "sort",
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
label: "描述",
|
||||
prop: "description",
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
label: "创建时间",
|
||||
prop: "createTime",
|
||||
width: 180
|
||||
}
|
||||
];
|
||||
// 初始化加载
|
||||
getList();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form ref="formRef" :inline="true" :model="searchFormParams"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]">
|
||||
<el-form-item label="分类名称:" prop="categoryName">
|
||||
<el-input v-model="searchFormParams.categoryName" placeholder="请输入分类名称" clearable class="!w-[200px]" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序:" prop="sort">
|
||||
<el-input-number v-model="searchFormParams.sort" :min="0" class="!w-[120px]" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :icon="useRenderIcon(Search)" @click="onSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm">
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar title="分类列表" @refresh="getList">
|
||||
<template #buttons>
|
||||
<el-button type="primary" :icon="useRenderIcon(AddFill)" @click="modalVisible = true">
|
||||
新增分类
|
||||
</el-button>
|
||||
<el-button type="danger" :icon="useRenderIcon(Delete)" :disabled="multipleSelection.length === 0"
|
||||
@click="handleBulkDelete">
|
||||
批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<el-table ref="tableRef" v-loading="loading" :data="dataList" row-key="categoryId"
|
||||
@selection-change="val => multipleSelection = val.map(item => item.categoryId)">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column v-for="col in columns" :key="col.prop" :prop="col.prop" :label="col.label" :width="col.width"
|
||||
:show-overflow-tooltip="col.showOverflowTooltip" />
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link :icon="useRenderIcon(EditPen)" @click="handleEdit(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-popconfirm :title="`确认删除【${row.categoryName}】?`" @confirm="handleDelete(row)">
|
||||
<template #reference>
|
||||
<el-button type="danger" link :icon="useRenderIcon(Delete)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
|
||||
:page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"
|
||||
@size-change="onSizeChange" @current-change="onCurrentChange" />
|
||||
</PureTableBar>
|
||||
|
||||
<category-form-modal v-model:visible="modalVisible" @refresh="getList" />
|
||||
<category-edit-modal v-model:visible="editVisible" :row="currentRow" @refresh="getList" />
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,195 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { ElMessage, FormRules } from "element-plus";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { GoodsDTO, updateGoodsApi, addGoodsApi } from "@/api/shop/goods";
|
||||
import { CategoryDTO, getCategoryAllApi } from "@/api/shop/category";
|
||||
import Confirm from "@iconify-icons/ep/check";
|
||||
import Upload from "@iconify-icons/ep/upload";
|
||||
const { VITE_APP_BASE_API } = import.meta.env;
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
row: {
|
||||
type: Object as () => GoodsDTO,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:visible", "refresh"]);
|
||||
|
||||
const formRef = ref();
|
||||
const isEdit = ref(false);
|
||||
const formData = reactive<GoodsDTO>({
|
||||
goodsId: 0,
|
||||
goodsName: "",
|
||||
price: 0,
|
||||
stock: 0,
|
||||
status: 1,
|
||||
categoryId: 0,
|
||||
goodsDetail: "",
|
||||
coverImg: ""
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
goodsName: [{ required: true, message: "商品名称必填", trigger: "blur" }],
|
||||
price: [
|
||||
{ required: true, message: "价格必填", trigger: "blur" },
|
||||
{ type: 'number', min: 0, message: '价格不能小于0' }
|
||||
],
|
||||
stock: [
|
||||
{ required: true, message: "库存必填", trigger: "blur" },
|
||||
{ type: 'number', min: 0, message: '库存不能小于0' }
|
||||
],
|
||||
coverImg: [
|
||||
{ required: true, message: "请上传商品封面图", trigger: "change" }
|
||||
]
|
||||
});
|
||||
|
||||
const handleConfirm = async () => {
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
if (isEdit.value) {
|
||||
await updateGoodsApi(formData.goodsId!, formData);
|
||||
ElMessage.success("商品修改成功");
|
||||
} else {
|
||||
await addGoodsApi(formData);
|
||||
ElMessage.success("商品添加成功");
|
||||
}
|
||||
emit("refresh");
|
||||
closeDialog();
|
||||
} catch (error) {
|
||||
console.error("表单操作失败", error);
|
||||
}
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
formRef.value.resetFields();
|
||||
emit("update:visible", false);
|
||||
};
|
||||
|
||||
const categoryOptions = ref<Array<CategoryDTO>>([]);
|
||||
|
||||
// 初始化表单数据和分类数据
|
||||
const initForm = async () => {
|
||||
try {
|
||||
// 加载分类数据
|
||||
const { data } = await getCategoryAllApi();
|
||||
categoryOptions.value = data;
|
||||
|
||||
// 初始化表单数据
|
||||
if (props.row?.goodsId) {
|
||||
isEdit.value = true;
|
||||
Object.assign(formData, props.row);
|
||||
// 确保数字类型
|
||||
formData.price = Number(formData.price);
|
||||
formData.stock = Number(formData.stock);
|
||||
} else {
|
||||
isEdit.value = false;
|
||||
if (data.length > 0) {
|
||||
formData.categoryId = data[0].categoryId;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("数据加载失败", e);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAvatarSuccess = (response, uploadFile) => {
|
||||
formData.coverImg = response.data.url;
|
||||
};
|
||||
|
||||
const beforeAvatarUpload = (rawFile) => {
|
||||
if (!['image/jpeg', 'image/png'].includes(rawFile.type)) {
|
||||
ElMessage.error('封面图必须是 JPG/PNG 格式!')
|
||||
return false
|
||||
}
|
||||
if (rawFile.size > 50 * 1024 * 1024) {
|
||||
ElMessage.error('封面图大小不能超过 50MB!')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
val => {
|
||||
if (val) initForm();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog :title="isEdit ? '编辑商品' : '新增商品'" :model-value="visible" width="600px" @close="closeDialog">
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="80px" label-position="right">
|
||||
<el-form-item label="商品名称" prop="goodsName">
|
||||
<el-input v-model="formData.goodsName" placeholder="请输入商品名称" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="封面图" prop="coverImg">
|
||||
<el-upload class="avatar-uploader" :action="VITE_APP_BASE_API + '/file/upload'" :show-file-list="false"
|
||||
:on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
|
||||
<img v-if="formData.coverImg" :src="formData.coverImg" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon">
|
||||
<Icon :icon="Upload" />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="价格" prop="price">
|
||||
<el-input-number v-model="formData.price" :min="0" :precision="2" controls-position="right" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="库存" prop="stock">
|
||||
<el-input-number v-model="formData.stock" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="商品分类" prop="categoryId">
|
||||
<el-select v-model="formData.categoryId" placeholder="请选择商品分类" clearable class="w-full">
|
||||
<el-option v-for="category in categoryOptions" :key="category.categoryId" :label="category.categoryName"
|
||||
:value="category.categoryId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="1">上架</el-radio>
|
||||
<el-radio :label="2">下架</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="closeDialog">取消</el-button>
|
||||
<el-button type="primary" :icon="useRenderIcon(Confirm)" @click="handleConfirm">
|
||||
确认
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.avatar-uploader {
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
|
@ -2,13 +2,14 @@
|
|||
import { ref } from "vue";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { getGoodsListApi } from "@/api/shop/goods";
|
||||
import { getGoodsListApi, GoodsDTO } from "@/api/shop/goods";
|
||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||
import Search from "@iconify-icons/ep/search";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
import GoodsFormModal from "./goods-form-modal.vue";
|
||||
import GoodsEditModal from "./goods-edit-modal.vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { deleteGoodsApi } from "@/api/shop/goods";
|
||||
|
||||
|
@ -37,6 +38,8 @@ const pagination = ref({
|
|||
const loading = ref(false);
|
||||
const dataList = ref([]);
|
||||
const multipleSelection = ref<number[]>([]);
|
||||
const editVisible = ref(false);
|
||||
const currentRow = ref<GoodsDTO>();
|
||||
|
||||
const getList = async () => {
|
||||
try {
|
||||
|
@ -115,6 +118,12 @@ const handleBulkDelete = async () => {
|
|||
const handleSelectionChange = (rows: any[]) => {
|
||||
multipleSelection.value = rows.map(row => row.goodsId);
|
||||
};
|
||||
|
||||
const handleEdit = (row: GoodsDTO) => {
|
||||
currentRow.value = row;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -171,7 +180,7 @@ const handleSelectionChange = (rows: any[]) => {
|
|||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link :icon="useRenderIcon(EditPen)">
|
||||
<el-button type="primary" link :icon="useRenderIcon(EditPen)" @click="handleEdit(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-popconfirm :title="`确认删除【${row.goodsName}】?`" @confirm="handleDelete(row)">
|
||||
|
@ -190,6 +199,7 @@ const handleSelectionChange = (rows: any[]) => {
|
|||
</PureTableBar>
|
||||
<!-- 新增商品弹窗 -->
|
||||
<goods-form-modal v-model:visible="modalVisible" @refresh="getList" />
|
||||
<goods-edit-modal v-model:visible="editVisible" :row="currentRow" @refresh="getList" />
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
|
|
Loading…
Reference in New Issue