|
@@ -0,0 +1,415 @@
|
|
|
+<template>
|
|
|
+ <div class="layout_container wxmall-package">
|
|
|
+ <header class="round-header">
|
|
|
+ <span>上架时间:</span>
|
|
|
+ <CyDateRange />
|
|
|
+ <span class="ml-12">
|
|
|
+ 套餐状态:
|
|
|
+ </span>
|
|
|
+ <el-select v-model="inquiry.state" clearable class="w-80">
|
|
|
+ <el-option label="已上架" value="ACTIVATED" />
|
|
|
+ <el-option label="已下架" value="DEACTIVATED" />
|
|
|
+ </el-select>
|
|
|
+ <el-divider direction="vertical" />
|
|
|
+ <el-button icon="Search" type="primary" @click="queryPackages">查询</el-button>
|
|
|
+ <el-button icon="Plus" color="green" @click="beforeAddPackage">上架</el-button>
|
|
|
+ </header>
|
|
|
+ <div class="layout_main layout_el-table">
|
|
|
+ <el-table :data="packageList" stripe>
|
|
|
+ <el-table-column prop="name" label="套餐名称" />
|
|
|
+ <el-table-column prop="price" label="套餐价格" />
|
|
|
+ <el-table-column prop="description" label="套餐描述" />
|
|
|
+ <el-table-column prop="createTime" label="创建时间" />
|
|
|
+ <el-table-column prop="createStaffName" label="创建人" />
|
|
|
+ <el-table-column label="状态">
|
|
|
+ <template #default="{row}">
|
|
|
+ <el-switch
|
|
|
+ v-model="row.state"
|
|
|
+ inline-prompt
|
|
|
+ style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
|
|
+ active-text="已上架"
|
|
|
+ inactive-text="已下架"
|
|
|
+ active-value="ACTIVATED"
|
|
|
+ inactive-value="DEACTIVATED"
|
|
|
+ @change="changePackageState(row)"
|
|
|
+ ></el-switch>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作">
|
|
|
+ <template #default="{row}">
|
|
|
+ <el-button
|
|
|
+ icon="View"
|
|
|
+ type="primary"
|
|
|
+ plain
|
|
|
+ @click="seePackageDetail(row.id)"
|
|
|
+ >
|
|
|
+ 查看
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ icon="Edit"
|
|
|
+ type="warning"
|
|
|
+ plain
|
|
|
+ @click="beforeEditPackage(row.id)"
|
|
|
+ >
|
|
|
+ 编辑
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ <el-drawer v-model="showCreation" :title="drawerTitle" size="50%">
|
|
|
+ <el-form :model="mainPackage" label-width="auto" :rules="packageMainRules">
|
|
|
+ <el-form-item label="套餐名称" prop="name">
|
|
|
+ <el-input v-model="mainPackage.name" maxlength="32" show-word-limit :disabled="disableEdit"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="套餐描述">
|
|
|
+ <el-input
|
|
|
+ v-model="mainPackage.description"
|
|
|
+ maxlength="256"
|
|
|
+ show-word-limit
|
|
|
+ :disabled="disableEdit"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="套餐图片" required prop="fileList">
|
|
|
+ <el-upload
|
|
|
+ :disabled="disableEdit"
|
|
|
+ class="avatar-uploader"
|
|
|
+ :show-file-list="false"
|
|
|
+ ref="uploadRef"
|
|
|
+ v-model:file-list="fileList"
|
|
|
+ :action="apiUrl + '/wxmallPackage/createNewPackage'"
|
|
|
+ :headers="header"
|
|
|
+ :data="creation"
|
|
|
+ :limit="1"
|
|
|
+ :on-change="handleSelectImage"
|
|
|
+ :on-exceed="handleExceed"
|
|
|
+ accept="image/jpeg,image/jpg,image/png"
|
|
|
+ :auto-upload="false"
|
|
|
+ :on-success="onSubmitSuccess"
|
|
|
+ >
|
|
|
+ <el-image v-if="imageUrl" fit="cover" :src="imageUrl" class="avatar-image" />
|
|
|
+ <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
|
|
+ <template #tip>
|
|
|
+ <div class="gray-text">
|
|
|
+ 请选择小于 200Kb 的 jpg/png 格式的图片。
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-upload>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <el-divider>
|
|
|
+ <el-tag>套餐明细</el-tag>
|
|
|
+ </el-divider>
|
|
|
+
|
|
|
+ <el-form
|
|
|
+ v-for="(item, index) in packageItemList"
|
|
|
+ :key="index"
|
|
|
+ :model="packageItemList[index]"
|
|
|
+ inline
|
|
|
+ :disabled="disableEdit"
|
|
|
+ label-width="auto"
|
|
|
+ >
|
|
|
+ <el-form-item label="名称" required>
|
|
|
+ <el-input v-model="item.hisName" readonly @click="displaySearchPanel(index)"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="单价" required>
|
|
|
+ <el-input v-model="item.price" type="number" class="w-80" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="数量" required>
|
|
|
+ <el-input v-model.number="item.quantity" class="w-80" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="!disableEdit">
|
|
|
+ <el-button
|
|
|
+ v-if="index === packageItemList.length - 1"
|
|
|
+ icon="Plus"
|
|
|
+ type="success"
|
|
|
+ @click="addNewItemLine(index)"
|
|
|
+ >
|
|
|
+ 添加
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ icon="Minus"
|
|
|
+ type="danger"
|
|
|
+ @click="deleteItemLine(index)"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <div style="flex: auto" v-if="!disableEdit">
|
|
|
+ <el-button @click="showCreation = false" size="default">取消</el-button>
|
|
|
+ <el-button @click="submitUpload" size="default" type="primary">提交</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-drawer>
|
|
|
+ <Search
|
|
|
+ v-if="showSearch"
|
|
|
+ med-type="01"
|
|
|
+ target="SFXM"
|
|
|
+ title="收费项目查询"
|
|
|
+ @close="showSearch = false"
|
|
|
+ @click-item="handleClickSearchItem"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import {
|
|
|
+ getPackagesByCondition,
|
|
|
+ getPackageDetail,
|
|
|
+ createNewPackage2,
|
|
|
+ updatePackageState
|
|
|
+} from "@/api/outpatient/wxmall-package.js";
|
|
|
+import useDateRange from "@/utils/cy-use/useDateRange";
|
|
|
+import {ElMessage, genFileId} from "element-plus";
|
|
|
+import {useUserStore} from "@/pinia/user-store";
|
|
|
+import env from "@/utils/setting";
|
|
|
+import Search from "@/components/search/Index.vue";
|
|
|
+
|
|
|
+const apiUrl = env.VITE_BASE_URL;
|
|
|
+
|
|
|
+const {CyDateRange, dateRange} = useDateRange({
|
|
|
+ shortcutsIndex: 2,
|
|
|
+ clearable: false,
|
|
|
+})
|
|
|
+
|
|
|
+const inquiry = reactive({
|
|
|
+ start: '',
|
|
|
+ end: '',
|
|
|
+ state: null
|
|
|
+})
|
|
|
+
|
|
|
+const packageList = ref([])
|
|
|
+function queryPackages() {
|
|
|
+ inquiry.start = dateRange.value.start
|
|
|
+ inquiry.end = dateRange.value.end
|
|
|
+ getPackagesByCondition(inquiry).then(data => {
|
|
|
+ packageList.value = data
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function changePackageState(row) {
|
|
|
+ updatePackageState(row).then(data => {
|
|
|
+ ElMessage.success(data)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const packageMainRules = reactive({
|
|
|
+ name: [ { required: true, message: '请输入套餐名称', trigger: 'blur' } ],
|
|
|
+})
|
|
|
+
|
|
|
+const mode = ref('see')
|
|
|
+const drawerTitle = computed(() => {
|
|
|
+ if (mode.value === 'add') {
|
|
|
+ return '上架套餐'
|
|
|
+ }
|
|
|
+ if (mode.value === 'see') {
|
|
|
+ return '套餐详情'
|
|
|
+ }
|
|
|
+ return '编辑套餐'
|
|
|
+})
|
|
|
+const disableEdit = computed(() => {
|
|
|
+ return mode.value === 'see'
|
|
|
+})
|
|
|
+const showCreation = ref(false)
|
|
|
+const mainPackage = ref({})
|
|
|
+const uploadRef = ref()
|
|
|
+const fileList = ref([])
|
|
|
+const imageUrl = ref('');
|
|
|
+function beforeAddPackage() {
|
|
|
+ mode.value = 'add';
|
|
|
+ showCreation.value = true
|
|
|
+}
|
|
|
+function handleSelectImage({raw}) {
|
|
|
+ if (raw.type !== "image/jpeg" && raw.type !== "image/png") {
|
|
|
+ ElMessage.error("请选择 jpg/png 格式的图片!");
|
|
|
+ uploadRef.value.clearFiles();
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (raw.size / 1024 > 200) {
|
|
|
+ ElMessage.error("图片大小不能超过 200Kb!");
|
|
|
+ uploadRef.value.clearFiles();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ imageUrl.value = URL.createObjectURL(raw);
|
|
|
+}
|
|
|
+function handleExceed(files) {
|
|
|
+ uploadRef.value.clearFiles();
|
|
|
+ const file = files[0];
|
|
|
+ file.uid = genFileId();
|
|
|
+ uploadRef.value.handleStart(file);
|
|
|
+}
|
|
|
+
|
|
|
+const packageItemList = ref([{quantity: 1}])
|
|
|
+const showSearch = ref(false)
|
|
|
+const currentIndex = ref(0)
|
|
|
+function displaySearchPanel(index) {
|
|
|
+ currentIndex.value = index
|
|
|
+ showSearch.value = true
|
|
|
+}
|
|
|
+function handleClickSearchItem(val) {
|
|
|
+ packageItemList.value[currentIndex.value].hisCode = val.code
|
|
|
+ packageItemList.value[currentIndex.value].hisName = val.name
|
|
|
+ showSearch.value = false
|
|
|
+}
|
|
|
+function addNewItemLine(index) {
|
|
|
+ let item = packageItemList.value[index]
|
|
|
+ if (!isValidItem(item)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ packageItemList.value.push({quantity: 1});
|
|
|
+}
|
|
|
+function deleteItemLine(index) {
|
|
|
+ packageItemList.value.splice(index, 1);
|
|
|
+}
|
|
|
+
|
|
|
+function isValidItem(item) {
|
|
|
+ if (!item.hisCode) {
|
|
|
+ ElMessage.error('请先填写项目名称')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if (!item.price || item.price < 0) {
|
|
|
+ ElMessage.error('项目单价不能小于 0')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if (!item.quantity || item.quantity < 1) {
|
|
|
+ ElMessage.error('项目数量不能小于 1,且必须为 整数')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+const addedItemCode = ref([])
|
|
|
+
|
|
|
+const header = {
|
|
|
+ token: useUserStore().getToken
|
|
|
+}
|
|
|
+const creation = ref({
|
|
|
+ mainPackageJson: null,
|
|
|
+ packageItemListJson: null
|
|
|
+})
|
|
|
+function submitUpload() {
|
|
|
+ addedItemCode.value = []
|
|
|
+ if (!mainPackage.value.name) {
|
|
|
+ ElMessage.error('套餐名称不能为空')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (fileList.value.length === 0 && mode.value === 'add') {
|
|
|
+ ElMessage.error('套餐图片不能为空')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (packageItemList.value.length === 0) {
|
|
|
+ ElMessage.error('套餐明细不能为空')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for (let i = 0; i < packageItemList.value.length; i++) {
|
|
|
+ let item = packageItemList.value[i]
|
|
|
+ if (addedItemCode.value.indexOf(item.hisCode) !== -1) {
|
|
|
+ ElMessage.error('请勿添加重复项')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ addedItemCode.value.push(item.hisCode)
|
|
|
+ if (!isValidItem(item)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ creation.value.mainPackageJson = JSON.stringify(mainPackage.value);
|
|
|
+ creation.value.packageItemListJson = JSON.stringify(packageItemList.value)
|
|
|
+ nextTick(() => {
|
|
|
+ if (fileList.value.length > 0) {
|
|
|
+ uploadRef.value.submit()
|
|
|
+ } else {
|
|
|
+ createNewPackage2(creation.value).then(res => {
|
|
|
+ onSubmitSuccess(res)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function resetFormData() {
|
|
|
+ mainPackage.value = {}
|
|
|
+ packageItemList.value = [{quantity: 1}]
|
|
|
+ creation.value = {
|
|
|
+ mainPackageJson: null,
|
|
|
+ packageItemListJson: null
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function onSubmitSuccess(res) {
|
|
|
+ resetFormData()
|
|
|
+ nextTick(() => {
|
|
|
+ imageUrl.value = ''
|
|
|
+ uploadRef.value.clearFiles();
|
|
|
+ ElMessage.success('提交成功')
|
|
|
+ if (mode.value === 'add') {
|
|
|
+ packageList.value.push(res.data)
|
|
|
+ } else if (mode.value === 'edit') {
|
|
|
+ queryPackages()
|
|
|
+ }
|
|
|
+ showCreation.value = false
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function seePackageDetail(id) {
|
|
|
+ getPackageDetail(id).then(res => {
|
|
|
+ mode.value = 'see';
|
|
|
+ mainPackage.value = res
|
|
|
+ packageItemList.value = res.items
|
|
|
+ imageUrl.value = res.thumbPath
|
|
|
+ showCreation.value = true
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function beforeEditPackage(id) {
|
|
|
+ getPackageDetail(id).then(res => {
|
|
|
+ mode.value = 'edit';
|
|
|
+ mainPackage.value = res
|
|
|
+ packageItemList.value = res.items
|
|
|
+ imageUrl.value = res.thumbPath
|
|
|
+ showCreation.value = true
|
|
|
+ })
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+.wxmall-package {
|
|
|
+ .ml-12 {
|
|
|
+ margin-left: 12px;
|
|
|
+ }
|
|
|
+ .w-80 {
|
|
|
+ width: 80px;
|
|
|
+ }
|
|
|
+ .gray-text {
|
|
|
+ color: gray;
|
|
|
+ }
|
|
|
+
|
|
|
+ .avatar-uploader {
|
|
|
+ .el-upload {
|
|
|
+ border: 1px dashed var(--el-border-color);
|
|
|
+ border-radius: 6px;
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ transition: var(--el-transition-duration-fast);
|
|
|
+ }
|
|
|
+ .el-upload:hover {
|
|
|
+ border-color: var(--el-color-primary);
|
|
|
+ }
|
|
|
+ .avatar-uploader-icon {
|
|
|
+ font-size: 28px;
|
|
|
+ color: #8c939d;
|
|
|
+ width: 100px;
|
|
|
+ height: 100px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ .avatar-image {
|
|
|
+ width: 100px;
|
|
|
+ height: 100px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+</style>
|