<template> <div class="good-page"> <div class="good-search" v-if="!isShowGoods"> <div> <a-radio-group v-model:value="status"> <a-radio-button value="3" class="status" @click="onSearch">全部</a-radio-button> <a-radio-button value="1" class="status" @click="onSearch">正常</a-radio-button> <a-radio-button value="2" class="status" @click="onSearch">已下架</a-radio-button> </a-radio-group> <a-input-search v-model:value="value" placeholder="请输入商品名称" style="width: 200px; margin-left: 10px" @search="onSearchName" /> </div> <div class="good-cad"> <a-button type="primary" class="mr10" @click="onGoods(0)">新建商品</a-button> <a-button type="primary" @click="onGoods(1)">新建商品包</a-button> </div> </div> <div class="goods-list" v-if="!isShowGoods"> <a-table :columns="columns" :data-source="list" :pagination="pagination" @change="tableChange"> <template #bodyCell="{ column, record }"> <template v-if="column.key === 'tags'"> <a-button type="primary" @click="onEdit(record)">编辑</a-button> <!-- <a-button type="primary" style="margin-left: 20px">统计</a-button> --> <a-popconfirm title="确定删除这条数据" ok-text="确定" cancel-text="取消" @confirm="onDel(record)"> <a-button type="primary" style="margin-left: 20px">删除</a-button> </a-popconfirm> </template> <template v-if="column.key === 'price'"> <div>{{ record.price / 100 }}</div> </template> </template> </a-table> </div> <div v-if="isShowGoods"> <div style=" display: flex; align-items: center; justify-content: space-between; " v-if="isShowGoods"> <a-button class="small left" @click="onBack"> <template #icon> <LeftOutlined /> </template> </a-button> </div> <GoodsBags :props="{ propsData }" v-if="propsData.isCad == 0" @onBack="onBack"></GoodsBags> <GoodsBagSubs :props="{ propsData }" v-else @onBack="onBack"> </GoodsBagSubs> </div> <a-modal v-model:visible="visible" title="编辑" :confirm-loading="confirmLoading" :width="1000" @ok="handleOk" :maskClosable="false" @Cancel="handleCancel" @handleChange="handleChange"> <a-form :model="modelRef" name="basic" :label-col="{ span: 2 }" :wrapper-col="{ span: 8 }" autocomplete="off"> <a-form-item label="商品名称" name="subject" :rules="[{ required: true, message: '请输入商品名称!' }]" :maxlength="200" :label-col="{ span: 2 }"> <a-input v-model:value="modelRef.subject" maxlength="30" /> </a-form-item> <a-form-item label="商品简介" name="shortname" :rules="[{ required: true, message: '请输入商品简介!' }]" :maxlength="200" :label-col="{ span: 2 }"> <a-input v-model:value="modelRef.shortname" maxlength="50" /> </a-form-item> <a-form-item label=" 商品示图" name="picurl" :wrapper-col="{ offset: 0, span: 8 }" class="Up" labelAlign="left" :label-col="{ span: 2 }"> <div class="clearfix"> <div style="display: flex;"> <a-upload v-model:file-list="fileList" :maxCount="1" action="http://43.142.42.187:8899/file/upload" list-type="picture-card" @preview="handlePreview" @change="handleChange"> <div v-if="fileList.length < 8"> <plus-outlined /> <div style="margin-top: 8px">Upload</div> </div> </a-upload> </div> <div> <p class="warin"> 建议图片比例为4:3,大小不超过20M,图片仅支持JPG、JPEG、PNG格式 </p> </div> </div> </a-form-item> <a-form-item label="商品详情" name="body" labelAlign="left" :label-col="{ span: 2 }"> <div style="position: relative;"> <div style="display: none;"> <a-upload class="clearfix-up-1" action="http://43.142.42.187:8899/file/upload" list-type="picture-card" :maxCount="1" @preview="onHandlePreview" @change="onEdithandleChange" :before-upload="onBeforeUpload"> <div ref="upImage" style="width:30px;height:30px;border: none;"><i class="iconfont icon-tupian" style="font-size:28px;"></i> </div> </a-upload> </div> <QuillEditor style="height:200px" ref="myQuillEditor" :options="options" :content="modelRef.body" content-type="html" @update:content="textChange" @focus="onEditorFocus($event)" @blur="onEditorBlur($event)" @change="onEditorChange($event)"> </QuillEditor> </div> <div class="editor-text"> <p>1、商品描述需符合《中华人民共和国广告法》</p> <p> 2、详情图片宽度750,高度不超过5000,每张图片最大不超过1M,图片仅支持JPG、JPEG、PNG格式。 </p> </div> </a-form-item> <a-form-item name="price" :wrapper-col="{ offset: 0, span: 4 }" label="商品售价" :rules="[{ required: true, message: '请输入输入价格' }]" labelAlign="left" :label-col="{ span: 2 }"> <div> <a-radio-group v-model:value="modelRef.free" @change="onSelectPlain"> <a-radio value="0">免费</a-radio> <a-radio value="1">收费</a-radio> </a-radio-group> </div> </a-form-item> <a-form-item :wrapper-col="{ offset: 2, span: 7 }" v-if="modelRef.free !== '0'"> <div style="display:flex;"> <div style="margin-right:20px">划线价</div> <a-input-number style="width: 200px" :min="0" string-mode :formatter="value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')" :parser="value => value.replace(/\$\s?|(,*)/g, '')" v-model:value="modelRef.markingprice" /> </div> </a-form-item> <a-form-item name="price" :wrapper-col="{ offset: 2, span: 7 }" :rules="[{ required: true, message: '请输入商品价格!' }]" v-if="modelRef.free !== '0'"> <div style="display:flex;"> <div style="margin-right:20px">售卖价</div> <a-input-number style="width: 200px" :min="0" string-mode :formatter="value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')" :parser="value => value.replace(/\$\s?|(,*)/g, '')" v-model:value="modelRef.price" /> </div> </a-form-item> </a-form> </a-modal> </div> </template> <script lang="ts" setup> import { ref, reactive, defineExpose, onMounted, watch, toRaw} from "vue"; import { LeftOutlined } from "@ant-design/icons-vue"; import { onSelectGoods, onUpdateGoods,onFindbyLikeName } from "@/api/index"; import { getUid } from "@/utils/userInfo"; import { Form, message, Upload, UploadProps } from 'ant-design-vue'; import { QuillEditor } from '@vueup/vue-quill' import '@vueup/vue-quill/dist/vue-quill.snow.css'; import GoodsBags from "./components/goods/index.vue"; import GoodsBagSubs from "./components/goodsubs/index.vue"; const useForm = Form.useForm; const myQuillEditor = ref<any>(null); const textarea = ref<any>('') let upImage = ref<any>(null) const columns = [ { title: "商品编号", dataIndex: "goodsid", key: "goodsid", }, { title: "商品名称", dataIndex: "subject", key: "subject", }, { title: "商品类型", dataIndex: "sellingmodel", key: "sellingmodel", }, { title: "商品售卖数", key: "sellcount", dataIndex: "sellcount", }, { title: "默认金额", key: "price", dataIndex: "price", }, { title: "已售金额", key: "sellamount", dataIndex: "sellamount", }, { title: "商品状态", key: "status", dataIndex: "status", }, { title: "操作", key: "tags", dataIndex: "tags", }, ]; const pagination = ref({ current_page:1, total:0, defaultPageSize:10, showSizeChanger:true, pageSizeOptions: ['5', '10', '15', '20'], onShowSizeChange:(current:any, pageSize:any)=>{pageSize = pageSize;} }) interface props { isCad: Number; } let options = reactive({ modules: { toolbar: { container: [ [{ size: ["small", false, "large"] }], ["bold", "italic", "underline"], [{ header: 1 }, { header: 2 }], [{ list: "ordered" }, { list: "bullet" }], ["link", "image"], [{ color: [] }, { background: [] }], [{ align: [] }], ], handlers: { image: function (value: any) { if (value) { upImage.value.click() } }, }, }, history: { delay: 1000, maxStack: 50, userOnly: false, }, }, }); const optionss = ref([]) const previewTitle = ref(""); const list = ref<any>([]); const value = ref<string>(""); const status = ref<string>("0"); const previewImage = ref(""); const previewVisible = ref(true); const modelRef = ref<any>({}); const propsData = reactive<props>({ isCad: 0, }); let isEditfileList = ref<any>([ ]); const fileList = ref<any>([ { uid: '-1', name: 'image.png', status: 'done', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ]); const isShowGoods = ref<Boolean>(false); const modalText = ref<string>("Content of the modal"); const visible = ref<boolean>(false); const confirmLoading = ref<boolean>(false); const showModal = () => { visible.value = true; }; const handleOk = async () => { const uid = getUid() const prarms = { uid: uid, goodsid: modelRef.value.goodsid, tenantid: modelRef.value.tenantid, goods_package_id: modelRef.value.parent, subject: modelRef.value.subject, shortname: modelRef.value.shortname, picurl: fileList.value[0].url, body: modelRef.value.body, price: modelRef.value.price * 100, markingprice: modelRef.value.markingprice * 100, status: modelRef.value.status, parent: modelRef.value.parent, free: modelRef.value.sellingmodel, sub: [0] } const data: any = await onUpdateGoods(prarms); if (data.state === 1) { visible.value = false; modelRef.value = {} await init(); } }; const handleCancel = () => { modelRef.value = {} visible.value = false; } const onSearch = async (e:any) => { status.value = e.target.value const uid = getUid(); const prarms: any = { size: 10, page: 1, strtus: e.target.value, uid, } const data:any = await onSelectGoods(prarms); list.value = data.data; pagination.value.total =data.count }; const tableChange=async(e:any)=>{ let queryData = { current_page:e.current,//当前页数 per_page:e.pageSize//每页显示条数 }; const uid = getUid(); const prarms: any = { size:queryData.per_page, page:queryData.current_page, strtus:status.value, uid, } const data = await onSelectGoods(prarms); list.value = data.data; } const onSearchName = async (e:any) => { const uid = getUid(); const params = { name: value.value, size:10, page:1, uid, }; const data = await onFindbyLikeName(params); list.value = data.data.goods; }; const onGoods = (e: any) => { propsData.isCad = e; isShowGoods.value = true; }; const onBack = async (e: Number) => { isShowGoods.value = !isShowGoods.value; // await onSearch({target:{value:'3'}}) }; const onEdit = (e: any) => { visible.value = !visible.value; modelRef.value.parent = e.parent modelRef.value.goodsid = e.goodsid modelRef.value.picurl = e.picurl modelRef.value.sellamount = e.sellamount modelRef.value.sellcount = e.sellcount modelRef.value.shortname = e.shortname modelRef.value.status = e.status modelRef.value.subject = e.subject modelRef.value.tenantid = e.tenantid modelRef.value.userid = e.userid modelRef.value.body = e.body modelRef.value.sellingmodel = e.price ? '1' : '0' modelRef.value.price = e.price / 100 modelRef.value.markingprice = e.markingprice / 100 modelRef.value.free = e.free fileList.value[0].url = e.picurl }; const { validate } = useForm( modelRef, ); const onSelectPlain = (e: any) => { if (e.target.value === '0') { modelRef.value.price = "0" modelRef.value.sellingmodel = e.target.value; } }; const onSubmit = () => { validate() .then((res) => { console.log(res, toRaw(modelRef)); }) .catch((err) => { console.log("error", err); }); }; const onDel = async (e: any) => { const uid = getUid(); e.status = "1"; e.uid = uid const data = await onUpdateGoods(e); await init(); }; const init = async () => { const uid = getUid(); const prarms: any = { size: 10, page: 1, strtus: 0, uid, } const data:any = await onSelectGoods(prarms); list.value = data.data.goods; pagination.value.total =data.count }; const textChange = (e: any) => { textarea.value = e } const onEditorFocus = (e: any) => { } const onEditorBlur = (e: any) => { } const onEditorChange = (e: any) => { console.log(e.html, 'e'); } const onHandlePreview = async (file: any) => { if (!file.url && !file.preview) { file.preview = (await getBase64(file.originFileObj)) as string; } previewImage.value = file.url || file.preview; previewVisible.value = true; previewTitle.value = file.name || file.url.substring(file.url.lastIndexOf("/") + 1); }; const onEdithandleChange = (info: any) => { let resFileList = [...info.fileList]; resFileList = resFileList.slice(-2); resFileList = resFileList.map((file) => { if (file.response) { return file; } }); if (!!resFileList) { isEditfileList.value = resFileList; } }; const onBeforeUpload: UploadProps['beforeUpload'] = file => { if (!(file.type.includes('png') || file.type.includes('jpeg') || file.type.includes('jpg'))) { message.error(`${file.name}不是png/jpef/jpg格式`); return Upload.LIST_IGNORE; } }; const handlePreview = async (file: any) => { if (!file.url && !file.preview) { file.preview = (await getBase64(file.originFileObj)) as string; } previewImage.value = file.url || file.preview; previewVisible.value = true; previewTitle.value = file.name || file.url.substring(file.url.lastIndexOf("/") + 1); }; const beforeUpload: UploadProps['beforeUpload'] = file => { if (!(file.type.includes('png') || file.type.includes('jpeg') || file.type.includes('jpg'))) { message.error(`${file.name}不是png/jpef/jpg格式`); return Upload.LIST_IGNORE; } }; const handleChange = (info: any) => { let resFileList = [...info.fileList]; // 1. Limit the number of uploaded files // Only to show two recent uploaded files, and old ones will be replaced by the new resFileList = resFileList.slice(-2); // 2. read from response and show file link resFileList = resFileList.map((file) => { if (file.response) { console.log(fileList, 'fileList'); } return file; }); fileList.value = resFileList; }; watch(isEditfileList, (newVal, old) => { if (newVal[0]) { const str = myQuillEditor.value.getHTML() + `<img src=${newVal[0].response.data}>` myQuillEditor.value.setHTML(str) modelRef.value.body = str } }) onMounted(async () => { await init(); }); defineExpose({ status, value, columns, previewImage, isShowGoods, propsData, modalText, visible, confirmLoading, modelRef, optionss, options, isEditfileList, previewVisible, textarea, upImage, pagination, tableChange, textChange, onEditorFocus, beforeUpload, onEditorChange, onEditorBlur, onSelectPlain, onSubmit, showModal, handleOk, handleCancel, onBack, onSearch, onGoods, onEdit, onDel, init, handlePreview, onHandlePreview, onEdithandleChange, onBeforeUpload, handleChange, }); </script> <style lang="less" scoped> .good-page { .good-search { width: 100%; display: flex; align-items: center; justify-content: space-between; .status { margin-right: 20px; width: 80px; border-radius: 5px; border: 1px solid rgba(0, 0, 0, 0.65); } .status::before { display: none; } .ant-radio-button-wrapper-checked { border: 1px solid @primary-color; } .ood-cad { width: 100%; } .ml10 { margin-left: 10px; } .mr10 { margin-right: 10px; } } .goods-list { margin-top: 10px; } .small { border: none; height: 30px; width: 50px; } .left { border-left: 1px solid rgb(236, 236, 236); border-top: 1px solid rgb(236, 236, 236); border-bottom: 1px solid rgb(236, 236, 236); border-right-width: 80%; border-right: 1px solid rgb(236, 236, 236); border-bottom-left-radius: 25px; border-top-left-radius: 25px; } } </style> <style lang="less"> .ant-layout-content { overflow-y: scroll; } </style>